wedge
As a python developer who can write functions, we are going to work in the company from today. Write a function and hand it over to other developers.
def func1(): print('in func1')
At the end of the quarter, the company's leaders are going to give everyone performance bonuses, so they propose to review the results developed by everyone during this period. What are the review standards? It is to count the execution time of each function.
What are you going to do at this time?
If you think about it, this is easy to do, change the function:
import time def func1(): start = time.time() print('in func1') print(time.time() - start) func1()
I came to the company for half a year, wrote 2000+ functions, and changed them one by one. One week has passed, and after the leaders have reviewed them, they will be deleted one by one. . . Another week has passed. . . Isn't this annoying?
You don't think so, you can't make yourself work hard and tell all the developers, now you all add a statement to calculate the time to your original code?
import time def func1(): print('in func1') start = time.time() func1() print(time.time() - start)
Or not, because it is too troublesome for development colleagues.
then what should we do? You have an idea and write a timer function. . .
import time def timer(func): start = time.time() func() print(time.time() - start) def func1(): print('in func1') def func2(): print('in func2') timer (func1) timer (func2)
Doesn't this look much simpler? No matter how many functions we write, we can call this timing function to calculate the execution time of the function. . . Although the cost of modification has become very small now, it still changes the calling method of this function for colleagues. If a colleague believes you and uses your method for 2w times in his code, then After he modifies the code, the boat of your friendship is completely overturned.
What you have to do is to let your colleagues still call func1, but to achieve the effect of calling the timer method.
import time def timer(func): start = time.time() func() print(time.time() - start) def func1(): print('in func1') func1 =timer #If it can be like this, it will be perfect. . . Sorry for the error func1()
It is a pity that the above code will report an error, because the timer method needs to pass a func parameter, we cannot pass the parameter when assigning, because as long as func1 = timer(func1) is executed, the timer method will be executed directly, the following The sentence func1 makes no sense at all. At this point, our thinking seems to have reached a deadlock. . .
The formation process of the decorator
import time def func1(): print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer (func1) func1()
After so busy for so long, it finally took shape! It's basically perfect now, the only thing that's annoying is that it's still doing an assignment call. . .
You think it's annoying, and python developers also think it's annoying, so we provide us with a syntactic sugar to solve this problem!
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer # ==> func1 = timer (func1) def func1(): print('in func1') func1()
Here, we can briefly summarize:
The essence of a decorator: a closure function
The function of the decorator: to extend the function of the original function without modifying the original function and its calling method
There is one last problem to be solved. The decorators we discussed just now are all decorating functions without parameters. Now what if we want to decorate a function with parameters?
def timer(func): def inner(a): start = time.time() func(a) print(time.time() - start) return inner @timer def func1(a): print(a) func1(1)
In fact, it is not difficult to decorate a function with parameters, but what if you have two functions that need to pass different parameters?
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'))
Now the problem of parameters has been solved perfectly, but what if your function has a return value?
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'))
The decorator just now is perfect, but the way we normally view some information about the function will fail here.
def index(): '''This is a homepage message''' print('from index') print(index.__doc__) #How to view function comments print(index.__name__) #How to view the function name
In order not to invalidate them, we have to add a little bit to the decorator to complete it:
from functools import wraps def deco(func): @wraps(func) #Add just above the innermost function def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''Ha ha ha ha''' print('from index') print(index.__doc__) print(index.__name__)
open closed principle
1. is open to extension
Why open up to extensions?
We say that it is impossible for any program to have all the functions in mind at the beginning of the design and not make any updates and modifications in the future. So we have to allow the code to expand, add new functionality.
2. is closed to modification
Why is it closed for modification?
As we just mentioned, because a function we wrote is likely to be delivered to other people for use, if we modify it at this time, it is likely to affect other users who are already using the function.
Decorators follow this open-closed principle perfectly.
The main function of the decorator and the fixed structure of the decorator
The main function of the decorator:
Add functions before and after the function without changing the way the function is called.
Fixed format for decorators:
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
from functools import wraps def deco(func): @wraps(func) #Add just above the innermost function def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
Decorator with parameters
Suppose you have thousands of functions that use a decorator, and now you want to remove all of them, what do you do?
Cancel one by one? Busy day and night for 3 days. . .
After two days, your leader has figured it out and let you add it. . .
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''What to do before executing the function''') re = func(*args,**kwargs) if flag: print('''What to do after executing the function''') return re return inner return timer @outer(False) def func(): print(111) func()
Multiple decorators decorate the same function
Sometimes, we also use multiple decorators to decorate the same function.
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()