This article brings you about python Knowledge about , It mainly sorts out the related problems of decorator functions , Including the forming process of the decorator 、 Essence and function 、 Advanced and optimization , Let's take a look at , Hopefully that helped .
- Recommend learning :python
Suppose I write a function f
def f():
print('hello')
Then I want to know how long this function will take to execute , It's easy to do , I just need to change the code to the following
import time
def f():
start = time.time() # Get the start time of program execution
print('hello')
end = time.time() # Get the end time of program execution
print(end - start) # Get the function f Execution time
f()
But then I wrote countless functions f2,f3……fn, I want to know how long each function takes to execute , So if you change everything like the above , Isn't it annoying ? Not yet. , Because it's too much trouble . Then what shall I do? ? So I had an idea , Wrote a timer function ...
import time
def timer(func):
start = time.time()
func()
print(time.time() - start)
def f():
print('hello')
def f2():
print('xorld')
timer(f)
timer(f2)
Isn't it a lot easier ? No matter how many functions we write, we can call this timing function to calculate the execution time of the function
But if I just want to do it the same way f1(),f2(),fn() Called this function , The function can also increase the function of calculation time on the premise that the original execution output result remains unchanged , Instead of calling timer(f),timer(f2) To calculate time , What should I do ?
After looking at the decorator function below, you will know how to solve this problem
Here is a simple version of the code that solves the above problem :
import time
def f():
print('hello')
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
f = timer(f)
f()
I just want to use the original way f1(),f2(),fn() Called this function , The function can also increase the function of calculation time on the premise that the original execution output result remains unchanged , But I'm still going to be in the function f Write before execution f = timer(f) In this string of code , Do you think it's an eyesore ?python The developers of the company also feel it's an eyesore , therefore python The developers of the java.com provide us with a syntax sugar to solve this problem !
use @timmer Instead of f = timer(f), This is a grammatical sugar .
import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer #==> Writing this sentence is equivalent to executing f = timer(f)
def f():
print('hello')
f()
1、 The essence
The essence of decorator is a closure function
2、 function
Extend the function of the original function without modifying the original function and its calling method
The decorators we just wrote are decorating functions without parameters , Now how to decorate a function with parameters ?
import time
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time() - start)
return inner
@timer
def f(a):
print(a)
f('hello')
In fact, decorating functions with parameters is not difficult , But if you have two functions , The parameters to be passed are different , such as function func1 There are two parameters func1(a ,b), function func 2 There is only one parameter func2(a), And they all want to decorate with this decorator , Calculate the function execution time ? So what do we do ? Then use the following code .
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
re = func(*args,**kwargs)
print(time.time() - start)
return re
return inner
@timer #==> func1 = timer(func1)
def func1(a,b):
print('in func1')
@timer #==> func2 = timer(func2)
def func2(a):
print('in func2 and get a:%s'%(a))
return 'fun2 over'
func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
Output results :
in func1
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
Now the parameter problem has been solved perfectly , But what if your function has a return value ? With the above code, you can't get the return value. How do you solve this problem ? Let's look at the following code !
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
re = func(*args,**kwargs)
print(time.time() - start)
return re
return inner
@timer #==> func2 = timer(func2)
def func2(a):
print('in func2 and get a:%s'%(a))
return 'fun2 over'
func2('aaaaaa')
print(func2('aaaaaa'))
Output results :
in func2 and get a:aaaaaa
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
Sometimes , We will also use multiple decorators to decorate the same function .
ef wrapper1(func): #func ----- f
def inner1():
print('wrapper1 ,before func')
func()
print('wrapper1 ,after func')
return inner1
def wrapper2(func):
def inner2():
print('wrapper2 ,before func')
func()
print('wrapper2 ,after func')
return inner2
@wrapper2 #f = wrapper2(f) ----->> wrapper2(inner1) == inner2
@wrapper1 #f = wrapper1(f) = inner
def f():
print('in f')
f() #===>>inner2
# Multiple decorators decorate the same function
Output results :
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
The decorator above is already very beautiful 了 , But there is a problem , If I add... To countless functions in my code @timer This syntax sugar , If I don't want to use it anymore, wouldn't it be necessary for everyone to annotate it , Working day and night 3 God ? Isn't it particularly troublesome , In order to better recycle decorators when they are not in use, instead of commenting or deleting them one by one We introduce the concept of decorators with parameters
'''
In order to better recycle decorators when they are not in use, instead of commenting or deleting them one by one
We introduce the concept of decorators with parameters
'''
import time
'''FLAGE Its purpose is to use it to control the switch of the decorator ,
So when we don't use it, we don't need to comment one by one, just put True Change it to False Just go '''
FLAGE = True
def timmer_out(flag):
def timmer(func):
def inner(*args,**kwargs):
if flag:
start = time.time()
ret = func(*args,**kwargs)
end = time.time()
print(end - start)
return ret
else:
ret = func(*args, **kwargs)
return ret
return inner
return timmer
@timmer_out(FLAGE)
#timmer_out(FLAGE)
# It is also equivalent to executing timmer_out(FLAGE)--->> return timmer———————>>@timmer(wahaha = timmer(wahaha))
def wahaha():
time.sleep(0.1) # If you don't rest, the function executes too fast to calculate the time
print('wahahahahahaha')
wahaha()
@timmer_out(FLAGE)
def erguotou():
time.sleep(0.1) # If you don't rest, the function executes too fast to calculate the time
print('erguotoutoutou')
erguotou()
Output results :
wahahahahahaha
0.10152268409729004
erguotoutoutou
0.10795140266418457
'''
print(wahaha.__name__) # View the function name in string format
print(wahaha.__doc__) # View the comments for a function
'''
# The following is used __name__ see holiday The function name of
from functools import wraps
def wrapper(func):
@wraps(func) # Right above the innermost function
def inner(*args,**kwargs):
print(' What to do before the decorated function is executed ')
ret = func(*args,**kwargs)
print(' What to do after the decorated function is executed ')
return ret
return inner
@wrapper #holiday = wrapper(holiday)
def holiday(day):
'''
This is a holiday notice
:param day:
:return:
'''
print(' All holidays %s God '%day)
return ' So happy '
print(holiday.__name__)
print(holiday.__doc__)
'''
The result is inner and None But what we want is to print holiday The string format of the function name and function comments ?
The solution is from functools import wraps
Using grammar is @wraps( Decorated function name )
'''
Output results :
holiday
This is a holiday notice
:param day:
:return:
1. The function extension of the original function is open
Why should we be open to functional extensions ?
For any program , It's impossible to have all the functions in mind at the beginning of the design without any update or modification in the future . So we have to allow later extensions 、 Add new features .
2. Closed to changes
Why is it necessary to close the amendment ?
As we just mentioned , Because we wrote a function , It is likely that it has been imported and used elsewhere , If we change it at this time , It is likely to affect other code that is already using this function .
The decorator perfectly follows this open and closed principle . This is the original intention of learning decorators
# Format 1
def timer(func):
def inner(*args,**kwargs):
''' What to do before executing the function '''
re = func(*args,**kwargs)
''' What to do after executing the function '''
return re
return inner
# Format two
from functools import wraps
def deco(func):
@wraps(func) # Right above the innermost function
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
- Recommend learning :python
- Reprinted from :www.php.cn/python-tuto…