This article mainly in-depth study of the Python decorator related Materials, what is the adorner? The principle that the adorner follows, has certain reference value, the interested small partner can refer to
What is an adorner?
In our software product upgrades, often need to add functionality to the various functions, and in our software products, the same function may be called hundreds of times, this situation is very common, if we modify each other, then our yards will not be hung off (someone said, you stupid, modify the function definition is OK!) Classmate, you wake up, if you want to add new features will modify the parameters, or return a value? )。 This time, it is time for our decorator to prowess. Adorners can be implemented, without changing the original function of the invocation form (that is, the function of transparent processing), the function of new functions. How it is implemented, and how it works, is explained below.
The principle that adorners follow
Decorator, as the name implies is the role of decoration, since it is decorated, then the object is decorated what kind of is what kind, can not have the slightest change. here, we write the adorner is must grasp cannot modify the modified function of the source code of this iron law. How to follow this iron law, we still need to do some paving, we must first understand the three concepts, as follows:
Function name is "variable"
In Python, the function name is actually like a C-language function pointer, which represents our function address, and only the interpreter obtains the address, and it executes the code of the memory. Therefore, in essence, the function name and the different variables are not very different, but the functions of the name and the ordinary variable refers to the memory of the use of the same way, these are the underlying interpreter of the mechanism of the decision, for the procedural apes, are transparent, so we can think that there is no difference between the two.
Higher order functions
What is a high-order function is actually very simple, grasping two principles is good:
As long as one of these two principles is met, it can be called a higher-order function. Look back, here is the name of the function we said above, carefully understand, we are here to treat it as an argument?
Nested Letters Number
What is nested function is also very simple, grasp a principle is good:
It should be emphasized here that the function definition does not execute the function body, and the definition of the variable is not to read the contents of the variable. This is very important and is very helpful for us to understand the decorator implementation principle.
How to write an adorner
With the above cushion, in the present to explain how to write the adorner, it is much better to understand.
Adorner essence
In fact, the adorner is essentially a function, it also has a function name, parameters and return values. But in Python, we use "@auth" to represent.
@auth # its equivalent to: Func = Auth (func) def func (): print ("func called")
This example is how to modify the format of the Func function in Python, and of course we have not implemented our adorner function. What we should pay attention to is what is written in the note, and we can see:
The adorner function is actually a higher-order function (both the parameter and the return value are the functions name).
"Auth (func)" is performed in the function of calling our adorner function, the adorner function, so be sure to keep this in mind.
Design ideas
The adorner is a function, and the equivalence relationship described above, then we can design our decorator:
In our decorator's function body to define a new function, within the new defined function to invoke the modified function, at the same time, in the context of the modified function to add new features. Finally, we use the return value of the adorner function to return the function name of our newly defined function.
As you know, the return value func in func = Auth (func) represents the function name of the newly defined function in the adorner.
Before doing a lot of bedding, is to reveal the implementation mechanism of the adorner here, in fact, nothing, very simple:
The adorner mechanism alters the address data represented by the function name of the modified function. to be blunt, the function name represents a memory block before being modified, and the function name represents a block of B memory when modified, except that a memory block is called when a block of B memory is executed. The code in the B memory block is our new feature. The implementation of this mechanism uses the mechanisms of "higher order functions" and "nested functions".
The end result is, but when the modified function is called, it is not the original memory block, but the new memory block that the decorator is requesting.
First step: Design Adorner functions
The adorner function definition is no different from the normal function definition, the key is how the function body writes the question. Here, for the sake of understanding, first with a parameterless adorner function description.
#装饰器函数定义格式def Deco (func): ' function body ... ' return func
There is no argument here, it means that there are no parameters other than "Func"
The difficulty is the writing of the function body, the following example first tells you why there is a second step:
#使用语法糖 @ To decorate the function, equivalent to "MyFunc = Deco (myfunc)" Def deco (func): print ("before MyFunc () called.") Func () print ("after MyFunc () called.") return func @decodef myfunc (): print ("MyFunc () called.") MyFunc () MyFunc () #output: Before MyFunc () Called.myfunc () Called.after MyFunc () Called.myfunc () Called.myfunc () called.
As can be seen from the output, our adorner does not take effect. Don't tell me. The adorner only takes effect once, which is the equivalent mechanism of "@deco" is ignored. When interpreted as "@deco", it is interpreted as "MyFunc = Deco (MyFunc)". Notice, as I mentioned earlier, that this is actually called the Deco function, so the function of Deco is executed. So the first three rows of output are not the effects of calling the MyFunc function, so how can you say that the adorner is in effect once? The second step is to solve the problem that the adorner does not take effect.
Step Two: wrapping the Modified function
#基本格式def Deco (func): def _deco () #新增功能 # ... #... Func () #别修饰函数调用 Return_deco
Here's an example:
#使用内嵌包装函数来确保每次新函数都被调用, #内嵌包装函数的形参和返回值与原函数相同, the adornment function returns the inline wrapper function Object Def deco (func): def _deco (): print ("Before MyFunc () called. ") Func () print ("after MyFunc () called.") # You do not need to return func, you should actually return the return value of the original function return _deco @decodef myfunc (): print ("MyFunc () called.") Return ' OK ' MyFunc () #output: Before MyFunc () Called.myfunc () Called.after myfunc () called.
Step Three: transparent processing of modified function parameters and return values
When the second step is completed, the decorator has finished the main part, and the following is the processing of the parameters and return values of the modified function. In order to truly implement the iron law of the adorner. Words not much to say, directly on the code:
#基本格式def Deco (func): def _deco (*args, **kwargs) #参数透明化 #新增功能 # ... #... res = func (*args, **kwargs) #别修饰函数调用 return res #返回值透明化 Return_deco
Through the above analysis, we know:
parameter transparency: When we call the decorated function, we actually call the _deco function here. So, let's add a variable parameter to the _deco function and pass the resulting variable argument to the Func function.
return value transparency: As with parameter transparency, define the return value for the _deco function and return the Func return value.
Transparent processing is so simple! At this point, our decorator is written. Let's give an example:
#对带参数的函数进行装饰, #内嵌包装函数的形参和返回值与原函数相同, the adornment function returns the inline wrapper function Object Def deco (func): def _deco (*agrs, **kwagrs): print ("Before MyFunc () called. ") ret = func (*agrs, **kwagrs) print ("after MyFunc () called". Result:%s "% ret" return ret return _deco @decodef MyFunc (A, b): print ("MyFunc (%s,%s) called."% (A, b))
return A + b print ("Sum=", MyFunc (1, 2)) print ("sum=", MyFunc (3, 4)) #output: Before MyFunc () called. MyFunc (called). After MyFunc () called. result:3sum= 3before MyFunc () called. MyFunc (3,4) called. After MyFunc () called. Result:7sum= 7
Adorner advanced
With parametric adorner
Adorners are also functions, so we can also pass parameters to them. What I'm saying here is: "@auth (auth_type = ' type1 ')" In this form yo. Let's start with the code:
#基本格式def deco (deco_type) def _deco (func): def __deco (*args, **kwargs) #参数透明化 #新增功能 # ... #... Print ("Deco_type:", Deco_type) #使用装饰器参数 res = func (*args, **kwargs) #别修饰函数调用 return res #返回值透明化 return __ Deco Return_deco
Plainly, it is in the original adorner based on the outermost set of a Deco function, and use it to receive the adorner parameters. Because it is in the outermost set of a function, then the function of the parameter scope is the function of the body inside, so the function in the definition of any use, so capricious.
So how do you understand the interpreter's parsing process? Here, as long as we understand a little, that is: "@auth (auth_type = ' type1 ')" is equivalent to "func = Auth (auth_type = ' type1 ') (func)" . The Interpreter first translates "auth (auth_type = ' type1 ')", and then returns the value (assuming that the non-existent function name is _func) as a functional pointer, where the _FUNC function name represents _deco and then executes "func = _func (func) ", and this Func function name represents the fact that __deco.
At this point, the purpose of transmitting the parameter through the adorner is achieved. Let's give an example:
#示例7: On the basis of example 4, let the adorner take parameters, #和上一示例相比在外层多了一层包装. #装饰函数名实际上应更有意义些 def deco (Deco_type): def _deco (func): def __deco (*args, **kwagrs): print ("before%s called [%s]. "% (func.__name__, deco_type)) func (*args, **kwagrs) print (" after%s called [%s]. "% (func.__name__ , Deco_type)) return __deco return _deco @deco ("MyModule") def myfunc (): print ("MyFunc () called.") @deco (" Module2 ") def MYFUNC2 (): print (" Myfunc2 () called. ") MyFunc () Myfunc2 () #output: Before MyFunc called [MyModule]. MyFunc () called. After MyFunc called [Mymodule].before myfunc2 called [Module2]. MYFUNC2 () called. After MYFUNC2 called [Module2].
Multi-Adorner modifier function
If you say that I understand what I said above, then this stuff is too simple. Is it to decorate the function that we are decorated with? But what I'm trying to say here is that we look at the problem from a different perspective. Our focus on the original modified function, we will find that NB Ah, I can add a number of functions to scatter. Let's give an example:
def deco (Deco_type): def _deco (func): def __deco (*args, **kwagrs): print ("before%s called [%s]."% (func._ _name__, Deco_type)) func (*args, **kwagrs) print ("after%s called [%s]."% (func.__name__, deco_type)) Return __deco return _deco @deco ("Module1") @deco ("MyModule") def myfunc (): print ("MyFunc () called.") @deco (" Module2 ") def MYFUNC2 (): print (" Myfunc2 () called. ") MyFunc () #output: Before __deco called [Module1].before myfunc called [MyModule]. MyFunc () called. After MyFunc called [MyModule]. After __deco called [Module1].
Note The results yo, @deco ("Module1"), to decorate the Deco ("Mymdule"), and we want to be the same, perfect!