This article is participating in Python Theme Month. See the link for details
What is a decorator
What is a closure? We can think of a decorator as a closure function that also returns a function.
You can also think of a decorator as passing a function as an argument to a method, processing it, and returning it.
Usage scenarios for decorators
1. Additional functions (unified log format, timer)
2. Data cleaning and adding (injecting data similar to dynamic token and modifying internal data cleaning)
3. Function registration, etc
Demo of decorator
1. How long it takes to implement a function before and after execution
Decorator use format: @decorator
Define the decorator
import time
def execTime(func) :
def inner(*args,**kwargs) : # receive parameters
time1 = time.time()
res = func(*args,**kwargs)
time2 = time.time()
print(func.__name__,'Execution cost',time2-time1,'s')
return res Return the value of func
return inner Return inner method object
Use the execTime decorator on the execAdd method
@execTime
def execAdd(a,b) :
time.sleep(1)
return a+b
# call execAdd
res = execAdd(1.2)
print(res)
Copy the code
Running results:
Python3 test.py execAdd execution cost 1.0000572204589844 s 3Copy the code
2. Realize unified log printing
import time,logging
def execTime(func) :
def inner(*args,**kwargs) :
time1 = time.time()
Add a log to the start of the method
logging.warning('now func {} start... '.format(func.__name__))
res = func(*args,**kwargs)
# add method end log
logging.warning('now func {} finished... '.format(func.__name__))
time2 = time.time()
print(func.__name__,'Execution cost',time2-time1,'s'.'\n')
return res
return inner
@execTime
def execAdd(a,b) :
time.sleep(1)
return a+b
@execTime
def sayHi(name) :
print('hello,{}.'.format(name))
sayHi('phyger')
res = execAdd(1.2)
print(res)
Copy the code
The output
python3 test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost0.00700068473815918s WARNING:root:now func execAdd start... WARNING:root:now func execAdd finished... ExecAdd Execution cost1.0020573139190674 s
3
Copy the code
You can see that both methods are added to the start and end alarm log before and after execution, and the execution time of the method is counted. This way we can easily encapsulate the method.
Pay attention to
When we execute the following code:
@execTime
def sayHi(name) :
print('hello,{}.'.format(name))
Copy the code
The output
python3 test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost 0.018001317977905273 s innerCopy the code
The name of the sayHi method is the inner function of the decorator, which is not what we expected. The name and comment of our method have been overridden by the decorator function. How can we fix this problem?
Python provided a function for me to solve this problem, which is functools.wraps.
Let’s rewrite the decorator to see what it looks like:
from functools import wraps
import time,logging
def execTime(func) :
@wraps(func)
def inner(*args,**kwargs) :
time1 = time.time()
logging.warning('now func {} start... '.format(func.__name__))
res = func(*args,**kwargs)
logging.warning('now func {} finished... '.format(func.__name__))
time2 = time.time()
print(func.__name__,'Execution cost',time2-time1,'s'.'\n')
return res
return inner
# call
sayHi('phyger')
print(sayHi.__name__)
Copy the code
The output
test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost 0.003000020980834961 s sayHiCopy the code
You can see that the __name__ value of the sayHi method using the decorator is already its own value.
If you want to add functionality to your current decorator, you might want to use a class decorator, because class inheritance is a great way to address this need. More on class decorators later.