1. Open Closure principle
In simple terms, it is对扩展开放,对修改封闭
In object-oriented programming, various functions are often defined.
The use of a function is divided into the definition phase and the usage stage, and after a function definition is complete, it may be called in many places.
This means that if the definition phase code of a function is modified, there will be a lot of impact, and it is easy to change the entire system because of a small local modification,
Therefore, for the modern process development industry, a system once on-line, the system's source code must not be able to change again.
However, after a set of systems on-line, with the increasing number of users, will certainly be a set of system extensions to add new features.
At this time, but also can not modify the original system source code, but also for the original system development to add new features, which is the program development industry, the open closure principle, it is necessary to use the adorner.
2. What is an adorner??
An adorner, as the name implies, is a tool for decorating and modifying other objects.
So the adorner can be any callable object, and the decorated object can be any callable object.
3. The role of the adorner
Add new functionality to a decorated object without modifying the source code of the decorated object and calling the method
Principle:
1.不修改被装饰对象的源代码2.不修改被装饰对象的调用方式
Goal:
为被装饰对象添加新功能
4. Definition and use of adorners
Look at the following code:
import time import random def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") index()
The function of the index function is that the program prints a word after 1-5 seconds of random sleep
Now you want to add a new function to the INDEX function: How do you do it by counting the run time of the index function??
Modify the index function as follows:
import time import random def index(): start_time=time.time() time.sleep(random.randrange(1,5)) print("welcome to index page") end_time=time.time() print("cost time: %s" %(end_time - start_time)) index()
Run the program and execute the results as follows:
welcome to index pagecost time: 2.000999927520752
As you can see, adding new functionality to the index function is true, but it violates the open closure principle.
If you want to add a new feature to the index function, you will need to use the adorner when it conforms to the open closure principle.
Modify Code
import time import random def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") def timmer(): def inner(): start_time=time.time() index() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner f=timmer() f()
Run the program to see the results of the execution
As can be seen from the program execution results, the run time of the index function has been counted.
But look at the source code can be known that the index function source code is not really modified, but the call method of index has been modified
And another problem is that the Timmer can only be used to decorate the index function, if you want to count the time of other functions, but also redefine the other adorners, it is too inflexible.
Modify the above code
import time import random def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") index=timmer(index) index()
Run the program to see the results of the program execution
As you can see, the source code of the index function has not been modified and the index function is not changed, but the function of statistical time is added to the index function, and the decorator is used here.
To analyze the execution flow of the above code:
1.导入time和random模块,定义index函数和timmer函数 2.把原始的index函数的内存地址作为参数传给timmer函数。 3.timmer函数内部嵌套定义一个函数inner,然后返回inner函数的内存地址 4.timmer函数执行完成,返回timmer函数的内部函数inner的内存地址,然后把inner的内存地址赋值给index变量 5.index是inner函数的内存地址,index变量加括号运行,实际上就是在运行inner函数 6.运行inner函数,定义程序开始时间。 7.执行timmer函数的变量func,在第2步知道,func这个变量就是index的内存地址,所以这里实际上是执行被装饰过后的index函数 8.index函数执行完成,定义程序的终止时间 9.统计并打印整个程序的执行过程中所花费的时间
This is the adorner decoration. The execution flow of the index function
5. Simplified use of adorners
Now I have another function home, now I also want to count the run time of the home function, you can change the code as follows
import time import random def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") def home(): time.sleep(random.randrange(1,5)) print("welcome to home page") index=timmer(index) index() home=timmer(home) home()
Run the program and execute the results as follows
As you can see, each time you invoke the Timmer of the Statistics program, the function name of the called function is passed to the Timmer adorner as a parameter.
Then the execution result of the Timmer decorator is assigned to the called function name itself, and finally can call the decorated function, too troublesome is there??
In fact, the adorner in Python can be simplified into the following format
import time import random def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner @timmer def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") @timmer def home(): time.sleep(random.randrange(1,5)) print("welcome to home page") index() home()
Program execution Results
As you can see, you @加装饰器名添加到被装饰对象的上方
can also add functions that are defined in adorners for a function
6. Definition and invocation of multiple adorners
In the above example, a timmer is defined and called by the adorner that runs the time of the statistic program,
If you want to add a user-authenticated feature to the index function now, you can define an adorner named Auth
import time import random def auth(func): def wrapper(): while True: user=input("Input your username>>>:").strip() pwd=input("Input your password>>>:").strip() if user== "abcd" and pwd == "abcd1234": print("login successful") func() break else: print("login error") return wrapper @auth def index(): time.sleep(random.randrange(1,5)) print("welcome to index page") index()
Run the program
Input your username>>>:abcd # 先输入错误的用户名和密码 Input your password>>>:1234 login error # 提示用户输入错误,登录失败 Input your username>>>:abcd # 让用户再次输入用户名和密码 Input your password>>>:abcd1234 login successful # 登录成功 welcome to index page # 执行index函数
As can be seen from the program execution results, the user login password Authentication Adorner auth has been defined and successfully called
If you want to add user authentication functionality to the index function, and you want to count the function of the index function execution time, how do I call it when I use an adorner?
import time import random def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner def auth(func): def wrapper(): while True: user=input("Input your username>>>:").strip() pwd=input("Input your password>>>:").strip() if user== "abcd" and pwd == "abcd1234": print("login successful") func() break else: print("login error") return wrapper @timmer @auth def index(): time.sleep(2) print("welcome to index page") index()
In the above code, for the index function added two adorners, now there is a problem, that is, which of the two adorners are called first, which is called after??
To analyze,
如果timmer装饰器先被调用,那么程序就会先执行timmer装饰器,然后再执行auth装饰器,提示输入用户名和密码, 这样一来timmer装饰器统计的时间就会包括输入用户名和密码的时间,这个时间会远远大于index函数睡眠的2秒种;如果auth装饰器先被调用,timmer装饰器后被调用,那么timmer装饰器统计的运行时间就应该只包括index函数的执行时间值应该在2秒多一点点的时间范围内
Run the program, first enter the wrong user name and password to use the program's execution time longer
From the execution of the program can be known, the program is to run the Timmer adorner, and then run the auth adorner, so Timmer statistics time includes the user authentication time, so Timmer statistics to the program run time is much larger than the index sleep 2 seconds
So here's a conclusion:
当一个函数同时被两个装饰器装饰时,加上函数最上面的装饰器先执行,加在下面的装饰器先装饰
Swap the Timmer adorner and the Auth adorner position in the example above.
import time import random def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner def auth(func): def wrapper(): while True: user=input("Input your username>>>:").strip() pwd=input("Input your password>>>:").strip() if user== "abcd" and pwd == "abcd1234": print("login successful") func() break else: print("login error") return wrapper @auth @timmer def index(): time.sleep(2) print("welcome to index page") index()
Run the index function, still enter the wrong user name and password, increase the user authentication time
As you can see, the time that this timmer is counted only includes the run time of the index function, not the time the user authenticates.
To analyze the example above, the index function is decorated by the Timmer decorator and the auth decorator. Decoration process
@auth # index=auth(timmer(index)) @timmer # index=timmer(index) def index(): time.sleep(2) print("welcome to index page")
It concludes that when a function is two adorners, the adorner is decorated
1.timmer装饰器装饰原始的index,可以写成:index=timmer(index)2.在timmer装饰器中,timmer装饰器实际上是返回inner的内存地址,所以在这里,index=inner3.timmer装饰器装饰完成后,由auth装饰器来装饰,此时可以写成index=auth(index),4.这里auth括号里的index已经不再是原始index函数,而是已经被timmer装饰过后的index了,所以index=auth(timmer(index))5.又因为timmer装饰的结果等于inner函数的内存地址,所以:index=auth(inner)
At this point, the decoration process of the two adorners has been known, to see the process of implementation of the procedure
6.程序先执行auth装饰器,进入用户认证,请用户输入用户名和密码7.用户输入正确的用户名和密码后,开始执行func函数,也已经上面分析的inner函数8.timmer装饰器先定义程序的开始运行时间,然后运行func函数,也就是原生的index函数9.index函数先睡眠2秒,然后执行print语句,再定义程序的结束时间10.最后统计并打印程序的运行时间,至此程序运行完毕。
So the time the user entered the user name and password is not counted by the Timmer decorator.
7. Setting and definition of decorated function parameters
Take a look at the code first
import time def timmer(func): def inner(): start_time=time.time() func() end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner @timmer def index(): time.sleep(2) print("welcome to index page") @timmer def home(name): time.sleep(3) print("welcome to %s home page" % name)
As shown above, the home function adds a parameter, and the index function has no parameters
The way to call the index function and the home function should be in this form, as normal functions are defined and called
index()home("python")
And then we run the program and we see that the program throws an exception
File "E:\python_learn\py_code\test.py", line 28, in <module> home("python")TypeError: inner() takes 0 positional arguments but 1 was given
Say an exception stating that the inner function does not require positional arguments, but we have given a positional parameter
Back to the part of the Timmer decorator definition, you can see that the intrinsic function of the Timmer decorator does not define the parameters
In this way, the Timmer adorner can only be used to decorate functions with no parameters,
We can add a parameter to the inner function when the Timmer adorner is defined
import time def timmer(func): def inner(name): start_time=time.time() func(name) end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner @timmer def index(): time.sleep(2) print("welcome to index page") @timmer def home(name): time.sleep(3) print("welcome to %s home page" % name) index() home("python")
However, when the Timmer adorner decorates the index function, it throws an exception because the index function has no parameters
File "E:\python_learn\py_code\test.py", line 27, in <module>index()TypeError: inner() missing 1 required positional argument: ‘name‘
Without knowing the number of parameters of the decorated function, that is, when the parameters of the decoration function are variable long and the form is not fixed,
can use *args
and **kwargs
, modify the above code
import time def timmer(func): def inner(*args,**kwargs): start_time=time.time() func(*args,**kwargs) end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner @timmer def index(): time.sleep(2) print("welcome to index page") @timmer def home(name): time.sleep(3) print("welcome to %s home page" % name) index() home("python")
Run the program again to see the results of the run
From the above, we can use *args
and **kwargs
to represent arbitrary lengths of parameters without knowing the number of parameters of the decorated function.
8. The return value of the decorated function
Modify the above code to define a return value for the home function, printing the return value of the index function and the home function, respectively
import time def timmer(func): def inner(*args,**kwargs): start_time=time.time() func(*args,**kwargs) end_time=time.time() print("run time: %s " %(end_time-start_time)) return inner @timmer def index(): time.sleep(2) print("welcome to index page") @timmer def home(name): time.sleep(3) print("welcome to %s home page" % name) return("home func") index_res=index() print(index_res) home_res=home("python") print(home_res)
Run the program and you can see
welcome to index pagerun time: 2.0 Nonewelcome to python home pagerun time: 3.0 None
As you can see, the return value defined in the Home function is not printed and the value shown is None
Because the home function executed here is not the original defined home function, but rather the result of the execution of the wrapper function
Because the wrapper function does not define a return value, the home function that was decorated does not print out the return value
Modify the code to define and return the return value that is performed by the adornment function in the Timmer adorner
import time def timmer(func): def inner(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) end_time=time.time() print("run time: %s " %(end_time-start_time)) return res return inner @timmer def index(): time.sleep(2) print("welcome to index page") @timmer def home(name): time.sleep(3) print("welcome to %s home page" % name) return("home func") index_res=index() print(index_res) home_res=home("python") print(home_res)
Execute the function again to see the results of the execution
welcome to index page run time: 2.0 None welcome to python home page run time: 3.0 home func
As you can see, the return value defined in the original home function is printed.
Conclusion:
如果被装饰函数没有定义返回值,timmer装饰器装饰后的返回值为None而如果被装饰函数定义了返回值,则timmer装饰器装饰后则返回被装饰函数的返回值
Python-Functional Programming decorator (I.)