Preface :
One 、 Decorator —— Formation process
Two 、 Decorator —— First knowledge of grammar sugar
3、 ... and 、 Decorator —— Essence and function
Four 、 Decorator —— Decorative belt parameters , Decorators that return values
1、 Decorate a function with one argument
2、 Decorate multiple functions with different parameters but no return value
3、 Decorate multiple functions with different parameters and return values
4、 Multiple decorators decorate the same function
5、 ... and 、 Decorator —— Advanced and optimized decorator
1、 Parameter decorator
2、 Prevent the necessary information of the function from invalidating
6、 ... and 、 Decorator —— Decoration principles
1、 Open and closed principle
1、 Fixed format of decorator ( Templates )
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 Our developers have provided us with a sentence Grammatical 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
Finally, thank you for seeing here :
In an unpublished article, Lu Xun said :“ If you understand the code, you must operate it yourself, so that you can better understand and absorb .”
One last sentence : A man can succeed in anything he has unlimited enthusiasm , Let's make progress together