본문 바로가기
Python

[Python] Decorator

by llHoYall 2021. 6. 29.

Decorator allows you to dynamically add actions to a function or object without changing the behavior of the function or object.

In Python, Decorator can be applied to a function and executed before and after the surrounding function.

Simple Example

This example changes the input string to the upper case.

def to_upper_case(func):
    text = func()
    if not isinstance(text, str):
        raise TypeError("Not a string")
    return text.upper()

@to_upper_case
def hello():
    return "hello"

print(hello)
# HELLO

We need to use a Decorator with @ symbol.

Multiple Decorators

Now, let's add one more Decorator.

def add_name(func):
    text = func()
    result = " ".join([text, "hoya"])
    return result

def to_upper_case(func):
    text = func()
    if not isinstance(text, str):
        raise TypeError("Not a string")
    return text.upper()

@to_upper_case
@add_name
def hello():
    return "hello"

print(hello)
# TypeError: 'str' object is not collable

We tried to add a name and got an error!

Now we have to use a wrapper function!

The wrapper function allows you to change the behavior of a function rather than executing the code.

So, several operations can be performed before a function is executed and after the function completes its execution.

def add_name(func):
    def wrapper():
        text = func()
        result = " ".join([text, "hoya"])
        return result
    return wrapper

def to_upper_case(func):
    def wrapper():
        text = func()
        if not isinstance(text, str):
            raise TypeError("Not a string")
        return text.upper()
    return wrapper

@to_upper_case
@add_name
def hello():
    return "hello"

print(hello())
# HELLO HOYA

The multiple Decorators are applied from bottom to top.

@add_name
@to_upper_case
def hello():
    return "hello"

print(hello())
# HELLO hoya

Therefore, changing the order of the Decorators changes the results.

Decorator with Arguments

We can use a Decorator with arguments.

def to_upper_case(func):
    def wrapper(*args, **kwargs):
        text = func(*args, **kwargs)
        if not isinstance(text, str):
            raise TypeError("Not a string")
        return text.upper()
    return wrapper

@to_upper_case
def hello(name):
    return f"hello {name}"

print(hello("hoya"))
# HELLO HOYA

You can see in the above example, just pass the arguments.

Decorator with Library

def mylogger(func):
    def logs(*args, **kwargs):
        print(func.__name__ + "() was called")
        return func(*args, **kwargs)
    return logs

@mylogger
def test(name):
    return f"hello {name}"

mytest = test("hoya")
# test() was called

print(test.__name__)
# logs

Oops! the information like __name__, __doc__ is lost.

You can consider using functools.wraps in this case.

from functools import wraps

def mylogger(func):
    @wraps(func)
    def logs(*args, **kwargs):
        print(func.__name__ + "() was called")
        return func(*args, **kwargs)
    return logs

@mylogger
def test(name):
    return f"hello {name}"

mytest = test("hoya")
# test() was called

print(test.__name__)
# test

Class Decorator

Python class can be used as a Decorator.

To make the call callable, Python provides a special method such as the __call__ method.

import functools

class ValidateParameters:
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func

    def __call__(self, *params):
        if any([isinstance(item, int) for item in params]):
            raise TypeError("Parameter should not be int")
        else:
            return self.func(*params)

@ValidateParameters
def add_numbers(*str_list):
    return "".join(str_list)

print(add_numbers("a", "b", "c"))
# abc

print(add_numbers(1, "d", "e"))
# TypeError: Parameter should not be int

Conclusion

We looked at the advantages of Decorator and how to use it.

'Python' 카테고리의 다른 글

[PyQt6] Getting Started  (0) 2021.08.10
[Python3] typing module  (0) 2021.07.03
[Python] Draw shapes using tkinter  (0) 2021.05.29
[Python] itertools module  (0) 2021.05.15
[Python] functools module  (0) 2021.05.08

댓글