Article Directory
Decorators are functions that modify the functionality of other functions
function
You can pass a function to a variable in Python
def func():
print("say hello")
a = func
print(a) # <function func at 0x0000025F271C6DC0>
a() # say hello
func() # say hello
Delete the original function and see what happens
del func
print(a) # <function func at 0x0000025F271C6DC0>
print(func) # name 'func' is not defined
We can also define a function inside the function and assign it to other variables (the function name is also used as a variable)
def func():
def say():
print("say hello")
return say
def not_say():
print("say goodbye")
not_say = func()
print(not_say) # <function func.<locals>.say at 0x0000025F271C68B0>
not_say() # say hello
say
As you can see, we assign the function to the function through the function callnot_say
first decorator
def func_decorator(a_func):
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
def my_func():
print("I am the function which needs some decoration to remove my foul smell")
my_func()
#outputs: "I am the function which needs some decoration to remove my foul smell"
my_func = func_decorator(my_func)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
my_func()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
This is what Python decorators do, they encapsulate a function and modify its behavior in one way or another, so @
how do you express it with the symbol?
@func_decorator
def my_func():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
my_func()
#the @func_decorator is just a short way of saying:
# my_func = func_decorator(my_func)
@func_decorator
In fact my_func = func_decorator(my_func)
, the function is to change the function of the current function (here is my_func
)
But there is a problem here. Doing so is equivalent to my_func
overwriting the original or directly intowrapTheFunction
print(my_func.__name__) # wrapTheFunction
This is not what we want, we just want to change the function of the original function, but still want to be able to access other properties of the original function
- Python provides functions
functools.wraps
@wraps
Accepts a function to decorate, and adds the ability to copy the function name, comment documentation, parameter list, etc. This allows us to access the properties of the function before the decoration inside the decorator
from functools import wraps
def func_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
@func_decorator
def my_func():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
print(my_func.__name__) # my_func
application
Logging
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
# Output: addition_func was called
class as decorator
To use a class as a decorator, a method needs to be defined inside the class __call__
.
__call__
Is a magic method that can call an instance of this class like a function
class Person:
def __init__(self) -> None:
self.num = 10
self.name = "nsy"
def __call__(self):
print(self.num)
a = Person()
a() # 10, 这里就是调用了 __call__ 方法
We use classes as decorators
class Person:
def __init__(self, func) -> None:
self.num = 10
self.name = "nsy"
self.func = func
def __call__(self, *args):
print("Process before my func")
self.func(*args)
print("Process after my func")
@Person
def say_name(name): # say_name = Person(say_name), 实例化了一个对象
print(f"my name is {
name}")
say_name("nsy") # 调用 __call__ 方法
- This is actually equivalent here
say_name = Person(say_name)
, which creates anPerson
object instance, and then wesay_name("nsy")
call__call__
the method when calling , so that the class is used as a decorator
Reference: Python function decorator | rookie tutorial (runoob.com)