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 |
댓글