First, the preparation of knowledge
To learn about adorners, you need to understand the following prerequisites:
function is variable
A function is essentially a variable, the function name is the variable name, the function body is the value corresponding to the variable, and the function body can be assigned as a value to other variables (functions), or it can be called directly by function name. The call symbol is ().
Nested functions
Inside a function, you can define one or more layers of functions, nested internal functions can be called inside the outer function body, or they can be returned directly as return values.
Closed Package
Within a nested function, a function that is nested internally can call an external function non-global and not be affected by an external function declaration period (that is, an external function non-global variable can be considered a direct call to a global variable).
Higher order functions
Pass a function as an argument to another function, and use the function name as the return value in order to invoke the function.
Variable assignment and invocation mechanism in Python
The procedure for defining variables in Python is as follows:
1. Allocate a piece of memory space in memory;
2. Store the value of the variable in this memory space;
3. Assign the address (house number) of this memory space to the variable name (that is, the variable name holds the memory space address)
Summary: The variable holds not the true value of the variable, but the memory space address where the real value is stored , which means that the call to the variable needs to be implemented by calling the memory space address ( house number). To extend a variable to a function, both functions and functions are variables, and when the calling function is passed, it is a simultaneous call to the two variables of the function and the argument, the mechanism of the variable assignment and invocation (indirect reference instead of the value corresponding to the direct call variable). The passing of a parameter is essentially a reference pass, which means that the delivery process is completed by passing a point to the address of the memory space where the argument resides.
This mechanism can be verified by ID ():
1List1 = [' Python ', ' PHP ', ' JAVA ']2List2 = List13 Print(ID (list1), ' ======== ', ID (list2))4 Print(‘‘)5 6 7 defFoo1 (x):8 Print(ID (foo1))9 Print(ID (x))Ten Print(FOO1) One A - defFoo2 (): -Pass the -Foo1 (Foo2 ()) - Print(‘---------‘) - Print(ID (foo1)) + Print(ID (Foo2 ())) - Print(FOO1) + AOutput: at7087176 ======== 7087176 #普通变量调用, the memory address points to the same - -7082192 -1348178992 -<functionFoo1 at 0x00000000006c10d0> ----------# before and after a function call, not only does the function name point to the same memory address, but the memory address of the argument and the formal parameter is the same in7082192 -1348178992 to<functionFoo1 at 0x00000000006c10d0>
Second, the demand background of the adorner
Imagine a real-world scenario in which your own applications have been running stably for a year, and later, as the business has developed, some of the functions defined by some functions need to be expanded, and the existing functions are called in many places as public interfaces.
Possible ways to implement:
1. Adjust the code to redefine functions that need to be modified
This requires hurt –, with the emphasis on ensuring the consistency of the code, and the possibility of redefining the original function call in such a way that it is not possible to load new functionality. You know this is a stable system on the line!
2. Encapsulate the new function as an acceptable function as an argument, call the original function at the same time, and then call back the higher order function by nesting function
Three, the definition of the adorner
As the name implies, adorners are used to decorate, it is also a function, but to receive other functions as parameters and decorate other functions, for other functions to provide additional functions. The most straightforward definition is that the adorner is actually a receive function as an argument and returns an executable function that replaces the function (details are discussed below).
Four, the role of the adorner and the application scenario
As mentioned above, adorners are decorated with other functions, providing additional functions that are not otherwise available to other functions. Quote a more detailed text: The adorner is a well-known design pattern that is often used for scenes with cut-off requirements, more classic insert logs, performance tests, transaction processing , and so on.
Decorators are a great design for solving such problems, and with adorners, we can pull out a lot of the same code that is not relevant to the function itself and continue to reuse it. In summary, the function of an adorner is to add additional functionality to an already existing object.
V. Principles for the definition and use of adorners
The following principles apply to the definition and use of adorners:
- Cannot modify the source code of the decorated function
- Cannot change the way the decorated function is called
The above two points are to guarantee the consistency and maintainability of the decorated function, and the extensibility and reusability of the new function (which is independent of the original function).
Six, the essence of the adorner
Let's start by combing the following points individually:
- How do adorners decorate other functions?
Through the characteristics of higher-order functions, the decorated function is passed as a parameter to the interior of the adorner, and then nested inside the adorner defines a function that is specifically used for decoration, which, while implementing the call to the decorated function, encapsulates the additional functionality that needs to be added.
- How does an adorner implement a call form that does not change to a decorated function?
When a decorated function is called inside the adorner, it is simple to invoke the decorated function as if the adorner concept was not introduced.
- Why does the adorner return a replacement function?
The adorner itself is also a function, although it has called the decorated function, and the encapsulation implements the additional functionality that needs to be added, but we need to use it as well as the normal function to invoke execution. In addition, after the call is executed to achieve our intended purpose, the return value of the adorner needs to include an implementation of the call execution of the decorated function and the addition of additional functionality. The preparatory knowledge has been elaborated, the time when the variable is called is implemented by reference to the memory space address of the variable name, so it is possible to return the function name (memory space address) of the adorner directly, so that the implementation can be called directly by calling the symbol () in the desired place.
When we pass the function that needs to be decorated to the adorner, the decorated function is essentially revolutionary, that is, Foo=wrapper (foo), although it looks the same as before the name being decorated, but the substance is the decorated function returned, that is, a replacement function is returned.
- Why do adorners require at least two nested functions?
Query the data can be found in the program example of the adorner when the adorner has designed at least two layers of nested functions, the outer layer is used to decorate the function as a parameter to pass in, the inner layer is the real decoration. Merging the two layers directly into one function is not a good way to merge the decoration function.
For example, verify that:
First come to the orthodox version of the decorator bar:
1 Import Time2 3 4 defTimmer (func): #外层函数传递被装饰的函数5 defWarpper (*args,**kwargs):6Start_time= Time. Time()7Func () #保持原样调用被装饰的函数8Stop_time= Time. Time()9 Print(' The Func run TimeIs%s '% (stop_time-start_time))Ten returnWarpper #把内层实际调用执行被装饰的函数以及封装额外装饰功能的函数名 (memory space address) is returned as a return value for subsequent calls to execute One A@timmer - defTest1 (): - Time. Sleep (3) the Print(' in the Test1 ') - -Test1 () - +Program output: -In the Test1 #原生方式调用执行被装饰的函数 +The Func Run Timeis 3.000171661376953 #附加了额外的程序执行时间统计功能
Note that the parameter form of the Warpper function defined here, the non-fixed parameter means that the actual incoming decorated function can have parameters and can have no parameters.
Now let's convert the two nested functions above into a function to try it out:
1 Import Time2 defTimmer (func):3Start_time= Time. Time()4Func ()5Stop_time= Time. Time()6 Print(' The Func run TimeIs%s '% (stop_time-start_time))7 returnTimmer #去掉内层嵌套函数warpper, return directly to Timmer itself8 9 Ten@timmer One A - defTest1 (): - Time. Sleep (3) the Print(' in the Test1 ') -Test1 () - -Program output: +In the Test1 -Traceback (most recent): +The Func Run TimeIs 3.000171661376953 AFile "e:/python_programs/s13/day4/deco.py", line A, in <Module> atTest1 () -Typeerror:timmer () Missing 1 required positional argument: ' Func '
Please note that after we have adapted the adorner, the program may be running, but it has already been made an error, suggesting that the last line is missing a positional parameter func when calling Test1 's decorated function: the return value of the adapted program
is Timmer itself, and we have defined a parameter func for it when we define the Timmer function, so we report a missing parameter error.
As for this parameter error, we can also support this by modifying the parameter form of the inner nested function:
1 Import Time2 defTimmer (func):3 defWarpper (x): #这里故意为内层函数定义一个参数4 Print(x)5Start_time= Time. Time()6Func ()7Stop_time= Time. Time()8 Print(' The Func run TimeIs%s '% (stop_time-start_time))9 returnWarpperTen One@timmer A defTest1 (): - Time. Sleep (3) - Print(' in the Test1 ') the -Test1 () - -Program output: +Traceback (most recent): -File "e:/python_programs/s13/day4/deco2.py", line +, in <Module> +Test1 () ATypeerror:warpper () Missing 1 required positional argument: ' x '
It can be seen that we modify the parameters of the inner layer function to report the same error, and directly cause the program to not run! The parameters of the inner layer function in our first demo program are non-fixed parameters and are optional, so run OK.
Do you remember the principle of the adorner above? One is not to change the way the call is performed on the decorated function (the original ecological call), but the source code of the decorated function is not changed. The problem with this adapted adorner is that the first original ecological call is not satisfied
The condition of the decoration function. To fix this problem, we can only return a function that has no parameters or can take no parameters as the return value , and the status quo is that the adorner function itself has been cured, must and can only be passed func one parameter so that it will be decorated
function is passed to the adorner, so we have to introduce an inline function with no parameters, and use it to complete the desired decoration and return the value for subsequent calls.
The adorner then becomes a two-tiered nested function, and the outer (first layer) function is responsible for passing functions that need to be decorated as parameters to the interior of the adorner, and defining the return value of the entire adorner, the inner layer (second layer) function is responsible for performing the specific decoration function, and the outer
The return value of righteousness is the inner nesting function that the inner layer actual original ecological call is decorated function and performs the extra adornment function. two layer nested division of labor clear and complementary, careful scrutiny of this design mode is really NB!
This is also the reason why many places say the higher order function + nested function = = Adorner.
Here is also an answer to a great God on the Web:
Original address: https://segmentfault.com/q/1010000006990592
- Why must the return value of the adorner be defined in the outer function? Is it possible to define in the inner layer function? The
First review the concept of the return value of the function, if the return value is not defined, then the function returns None, at which point the type of the function is Nonetype. When we define the return value in the inline function instead of defining the return value in the outer layer function, the outer function does not have a return value, and the type itself becomes nonetype, and the actual invocation is reported as "TypeError: ' Nonetype ' object is not callable" The error. An established fact is that, although the function that actually performs the adornment function is an inner function, we call it as an outer function, otherwise the decorated function cannot be passed to the adorner!
OK, here's a few points to review the adorner is a kind of step-by-step alarming, interlocking, seamless bright?
- What's a grammar candy?
A little attention to detail is not difficult to find, the adorner is defined by the way it is called by @decorator, which often appears in front of the decorated function. This is the legendary grammar of sugar. For example, the sample program above, @timmer, is exactly equivalent to Test1=timmer (test1), which is the essence of the adorner returning a replacement function. Here are two details to note:
1. Assigning a value to a test1 is a function name (house number, memory space address) passed by reference.
2. The following also needs to be called to really implement the decoration of the Test1, the call method is test1 plus the call symbol ()
But please note that the grammatical sugar is not a big thing, do not think that @ has another magic. In addition to the less character input, there is an additional benefit: it looks more like a decorator.
At this point, some of the decorative elements have been elaborated, it is time to make some summary of the adorner.
Decorator Summary:
The adorner itself is an executable function, but a higher-order function;
The adorner uses the receiving function as the parameter and combines the nested function to realize the decoration of the decorated function.
The nesting function of the adorner should have at least two layers, the outer layer receives the decorated function as a parameter, passes it to the inner layer, and return the inner function (substitution function) back for subsequent calls to decorate; the interior finishes the original ecological call to the decorated function and the custom additional decoration function;
The essence of the replacement function returned by the adorner is that the inner layer of the nested function not only realizes the original ecological call to the decorated function, but also adds the function of the expected decoration, and changes the function body (the value of the variable) without changing the name of the decorated function (variable name) under the premise of the decorator.
The adorner itself can only receive the decorated function as the only parameter, but can define additional parameters in the inner function to achieve the purpose of decorating the function with parameters, but also can add a layer of nesting in the outer layer and define other parameters for the adorner (as shown in the program example below);
The function of the decorator is all embodied in the decoration two words.
Vii. Decorator Program Example (decorative parameterless function, decorated with parametric functions, the adorner comes with parameters)
- Decorate a function without parameters
Let's have a different chestnut:
1 defDeco1 (func): #外层函数把被装饰的函数作为参数引入2 defWrapper ():3 Print(' Begin----')4Func () #内嵌函数开始调用执行被装饰的函数, called in the original ecological5 Print(' End-----')6 returnWrapper # The function returned here is the replacement function, which contains the logic to execute the original function call and add additional decorative functions, note that the function name is returned (house number)7 8 9@deco1Ten defTest1 (): One Print(' This was for Test ') A -Test1 () #这里的test1在执行时会被替换为装饰器中的wrapper函数, is not a test1 function defined in the original sense, the TEST1 function defined in the original sense corresponds to the func () in the wrapper.
#这里通过调用符号 () to invoke the Test1 function after being replaced, notice that the return value in the adorner is the memory space address (house number) of the wrapper, which is the replacement function, which can be obtained by calling the symbol ().
function body (assigning value to variable test1) - theProgram output: -Begin---- -This was for test -End-----
See if this example does not feel that decorating a function without parameters is simpler? Although this adorner looks not much practical or even a little low, it is enough to demonstrate the decorator process.
- Functions decorated with parameters
When decorating a function with parameters, the parameter needs to be received in the adorner's inner function.
To visually demonstrate the relevant logic, directly on the chestnut with a non-fixed parameter:
1__author__ = ' Beyondi '2#!/usr/bin/env python3#! -*-Coding:utf-8-*-4 5 6 defDEC2 (func): #外层函数只能处理被装饰的函数这一个参数7 defWrapper (*args, **kwargs): #被装饰的函数的参数, be sure to introduce processing in inline functions8 Print(' Begin to decorate ... ')9ret = func (*args, **kwargs)Ten Print(' Arguments is%s%s '% (args, Kwargs) One returnRet A returnWrapper - - the@dec2 - defTest2 (x, Y, z): - Print(' AAA ') - return2 + -Test2 (' A ', ' B ', z= ' C ') #实际调用时被装饰的函数参数传递方式不变 + AProgram output: atBegin to decorate ... -Aaa -Arguments is (' A ', ' B ') {' Z ': ' C '} -
Fixed the function decoration of the non-fixed parameter, the function adornment of fixing parameter is of course simpler.
- Decorator comes with parameters
The focus of the chestnut above is on whether the decorated function has parameters, in practical applications to make the adorner more powerful and comprehensive, often need to give the adorner also define parameters, in order to perform more complex and flexible logic. The following example program determines the length of the actual argument passed by the decorated function on the basis of the above program:
1 defDeco (limit): #装饰器自带的参数需要再定义一个外部函数来引入2 defDEC2 (func): #接收处理被装饰的函数变量3 defWrapper (*args, **kwargs): #处理被装饰的函数传递的参数, logic invariant4 Print(' Begin to decorate ... ')5#Print(args)6Func (*args, **kwargs)7 ifLen (args) >= limit: #装饰器自带的参数开始派上用场8 Print(' Arguments OK ')9 Else:Ten Print(' Arguemts error ') One returnWrapper A returnDec2 - - the@deco (2) #通过语法糖进行装饰时, it is necessary to pass the parameters of the adorner to change the final output of the actual participant impact program. - defTest2 (x, Y, z): - Print(' AAA ') - return2 + -Test2 (' A ', ' B ', z= ' C ') + AProgram output: atBegin to decorate ... -Aaa -Arguments OK # Program output results in line with expectations
The above procedure shows that the introduction of parameters to the adorner itself can achieve a more flexible and powerful decorative effect. It is important to note that the adorner's own parameters must be introduced in the outermost definition of the adorner, when the true adorner
is the innermost nested function. This is why the adorner is at least a higher-order function that requires double-layered nesting.
Python Learning Path day4-functions advanced features, adorners