Detailed Python decorator _python

Source: Internet
Author: User

The function of the adorner in many languages, the name is not the same, in fact it embodies a design pattern, emphasizing the open closure principle, more for later function upgrades rather than writing new code. Adorners can not only decorate functions, but also decorate other objects, such as classes, but usually, we use the decoration function as an example to introduce its usage. To understand the principle of adorners in Python, it takes a step by step. This article as far as possible to describe the simple and understandable, from the most basic content.

(Note: The following use Python3.5.1 environment)

First, Python's function-related basics

First, it must be emphasized that Python is executed from the top down, and that the code block that touches the definition of a function does not execute it immediately, and it executes its internal code block until the function is called.

def foo ():
print (the Foo function is running!). If that's the 
case, the statements in Foo will not be executed. The
program simply reads the definition code block into memory.

Take another look at the sequential execution of the example:

def foo ():
 print ("I am the function definition above!") ")
def foo ():
 print (" I am the following function definition! ") ")
foo ()
run result:
I am the following function definition

Visible, because of sequential execution, the following Foo overwrites foo above. Therefore, in Python the placement of the code is required, can not be placed at will, the function body to be placed before the called statement.

Secondly, we have to figure out a few things first: function name, function body, return value, function memory address, function name parenthesis, functions name is used as parameter, function name parenthesis is used as parameter, return function name, return function name plus parenthesis. for the following functions:

 def foo ():
 print ("Let's do something!") ") return
 " OK "
 foo ()

Name of function: Foo

function Body: 第1-3 line

return value: string "OK" if no return object is explicitly given, the default returns none

The memory address of the function: when the function body is read into memory, it is referenced by the identifier, the name Foo,
That is, Foo is pointing to the location where the function body is stored in memory.

Function name parenthesis: for example, Foo (), the function invocation method, only see this bracket, the program will be based on
The function name finds the body of the function from memory and executes it

Let's look at the following example:

def outer (func):
 def inner ():
 print ("I am the inner layer function!") ") return
 inner
def foo ():
 print (" I am the original function! ") ") 
outer (foo)
outer (foo ())

In Python, everything is an object, and the function is no exception. You can therefore call the function name and even the function name with parentheses as the return value of another function. In the code above, outer and Foo are two functions, and outer (foo) means to pass the function name of the Foo function as an argument to the outer function and execute the outer function; outer (foo ()) To pass the return value of the Foo function as a parameter to the outer function and execute the outer function, since the Foo function does not specify a return value, it is actually passed to the outer function as a none. Note the difference, there is no parenthesis is the key!

Again, inside the outer function, a inner is returned, which is a function defined inside the outer function, and note that since inner is not followed by parentheses, it returns the function body of the inner, which is actually the name of inner, a simple reference. So, what if the outer function returns inner ()? Now you should be very clear, it will first execute the contents of the inner function, and then return a none to Outer,outer and then return the none to the object that called it.

 Remember that the function name, function parenthesis can be passed as a parameter, or it can be returned as a return value, and there are two distinct meanings in parentheses!

Second, the use of the decoration of the scene

Adorners are often used to add additional functionality to the original function code and functionality without altering it. For example, do something before the original function executes, and perform something after execution.

Let's take a look at the use of the adorner and the design pattern embodied by an example. (I'm sorry I can't design a better scene, I can only quote Wu God's case)

There is a large company, the subordinate base Platform department responsible for internal application and API development, there are hundreds of business units responsible for different business, they each invoke the basic Platform Department to provide different functions to handle their own business, as follows:

# The Base platform department has developed hundreds of function
def F1 ():
 print ("Business Unit 1 Data Interface ...")
def f2 ():
 print ("Business Unit 2 Data Interface ...")
def f3 ( ):
 Print ("Business Unit 3 Data Interface ...")
def F100 ():
 print ("Business Unit 100 Data Interface ...") 
#各部门分别调用
F1 ()
F2 ()
F3 ()
F100 ()

Because the company in the initial stage of start-up, the basic platform for the development of these functions, for a variety of reasons, such as time, such as ill-conceived, and so on, no security certification for function calls. Now, the director of the Platform decided to make up for the flaw, so:

The first time, the supervisor called a maintenance engineer, engineers ran up and down the department to inform, let them in the code with the authentication function, however, the same day he was expelled.

The second time: the manager called a maintenance engineer, the engineer used the shell to write a complex script, reluctantly realized the function. But he soon went back to do Yun-Wei, will not develop the Yun-Wei is not good luck dimension ....

The third time: the director called a Python automation development engineer, the man is doing this: only the basic platform of the Code refactoring, so that N business units do not need to make any changes . The man was soon opened, and Lian Yunwei had nothing to do.

Def f1 ():
 #加入认证程序代码
 Print ("Business Unit 1 Data Interface ...")
def f2 ():
 # Add authentication program code
 print ("Business Unit 2 Data Interface ...")
def f3 ():
 # Add authentication program code
 print ("Business Unit 3 Data Interface ...")
def F100 ():
 #加入认证程序代码
 Print (" Business Unit 100 Data Interface ... ")
#各部门分别调用
F1 () F2 () F3 ()
F100 ()

Four back: The supervisor changed another engineer, he is so dry: Define an authentication function, the original other function called it, the code is as follows box. But the supervisor was still dissatisfied, but this time he explained why. The director said: Write code to follow the open closure principle , although in this principle is mainly for object-oriented development, but also for functional programming, in simple terms, it stipulates that the implementation of the function code is not allowed to be modified, but external can be extended, namely: Closed: Implemented functional code block ; Open: open to expansion. If the open closure principle is applied to the above requirements, then code modification within the function F1, F2, f3......f100 is not allowed. Unfortunately, the engineer did not have a beautiful girlfriend, so she was soon expelled.

def login ():
 print ("Certified successful!") ")
def F1 ():
 login ()
 print (" Business Unit 1 Data Interface ... ")
def f2 ():
 login ()
 print (" Business Unit 2 Data Interface ... ")
def f3 ():
 login ()
 print ("Business Unit 3 Data Interface ...")
def F100 ():
 login ()
 print (Business Unit 100 Data Interface ... ")
#各部门分别调用
F1 () F2 () F3 ()
F100 ()

Five: There is no time for the supervisor to find someone else to do the job, he decided to go in person, and he intends to add a log function after the function is executed. That's what the supervisor thinks: No decorator's supervisor is not a good code farmer! Why else would I be in charge, and you'd be in control? Hey. His code is as follows:

#/usr/bin/env python
#coding: utf-8
def outer (func):
 def inner ():
 print ("Certified success!") Result
 = func ()
 print ("Log add succeeded") return result return
 inner
@outer
def f1 ():
 Print ("Business Unit 1 Data Interface ...")
@outer
def f2 ():
 print ("Business Unit 2 Data Interface ...")
@outer
def f3 ():
 Print ("Business Unit 3 Data Interface ...")
@outer
def f100 ():
 print ("Business Unit 100 Data Interface ...")
#各部门分别调用
F1 ()
F2 ()
F3 ()
F100 ()

For the above code, it is only necessary to expand the code of the base platform, you can implement the other departments call function F1 F2 F3 F100 before the authentication operation, save the log at the end of the operation, and other business units do not need their own code to make any changes, call the way to change. "Director" After writing code, feel that the music is not as good as the public Lele, intends to show off, so wrote a blog will process a detailed description.

Third, the interior principle of the adorner,

Let's take the F1 function as an example to illustrate the following:

 def outer (func):
 def inner ():
 print ("Certified successful!") Result
 = func ()
 print ("Log add succeeded") return result return
 inner
@outer
def f1 ():
 print (" Business Unit 1 Data Interface ... ")

Use the knowledge we introduced in the first section to analyze the above code:

    • The program starts running, compiles from the top down, reads Def outer (func): and discovers that this is a "first class citizen"-> function, then loads the function body into memory and then over.
    • When I read the @outer, the program was attracted by the syntax sugar of @, knowing that it was an adorner, followed by the rules for immediate execution, and the program began to run the function defined by the name outer after the @. (believe that no one will be stupid to write @outer to another location, it can only be placed on the top of the decorated function most recently, do not empty lines.) )
    • The program returns to the outer function and starts executing the adorner's grammar rules, which is the rule of the Python "law" and don't ask why. The rule is that the name of the decorated function is passed as an argument to the adornment function. When the adorner executes its own internal code, it assigns its return value to the decorated function.  

As shown in the following illustration:

Here's what you need to be aware of:

    • @outer and @outer () are different, without parentheses, the outer function will still be executed, which differs from traditional parentheses in order to call a function, requiring special attention! So there's parentheses? That is the advanced usage of the adorner, which will be introduced later.
    • It is F1 that the function name (instead of F1 () is called) is passed to the adorner outer as a parameter, that is, Func = f1, @outer equals outer (F1), actually passing the function body of the F1 instead of the return value after the F1 is executed.
    • The outer function returns the function name of inner, not the return value of inner () after being invoked.

If you have a clear understanding of the basics of the first part of the function, you should be able to easily understand the above.

4. The program began to execute the contents of the outer function, and at first it came across a function, very round, right? Of course, you can arrange some other code before and after the inner function, but they are not the focus, and there is a little trouble, as explained below. Inner function definition blocks are not executed immediately after being observed by the program, but are read into memory (this is the unspoken rule).

5. Further down, hit return inner, the returned value is a function name, and the function name will be assigned to F1 this decorated function, that is, F1 = inner. According to the previous knowledge, we know that at this time the F1 function is overwritten by the new function inner (actually F1 the function name to change exponentially to the function body that points to inner this function name, F1 no longer points to the memory address of its original function body), When you call F1 later, the code within the inner function is executed, not the previous function body. So where are the previous function bodies? Remember when we passed the F1 as parameters to func this formal parameter? Func This variable saves the address of the old function in memory, it can execute the old function body, you can see the code of result = Func () in the inner function, that's what it does!

6. Next, it's not over yet. When the business unit is still calling the F1 function by F1 (), the execution is no longer the code of the old F1 function, but the code of the inner function. In this case, it first prints a "certified success" prompt, obviously you can switch to arbitrary code, which is just an example; then it executes the Func function and assigns the return value to the variable result, the Func function is the old F1 function, and then it prints the "log Save" prompt, This is also just an example that can be replaced with whatever you want, and finally return the variable of result. We can accept the value of result in the code of the business unit with R = F1 ().

7. After the process has gone, you should have seen it, without any modification to the code and interface calls of the business department, there is no internal modification to the original code of the basic Platform department, just adding an ornament function, realizing our requirement, authenticating before function call, and writing log after calling. This is the biggest role of the adorner.

Question: So why do we have a outer function that is so complicated with a inner function? A layer of function not?

A: Please note that @outer this code will automatically execute the code inside the outer function, if not encapsulated, in the business unit has not been called, the implementation of what, this is a bit inconsistent with the original intention. Of course, if you have a demand for this, it's not impossible. Take a look at the example below, which has only one layer of functions.

def outer (func):
 print ("Certified success!") Result
 = func ()
 print (log add success) return result
@outer
def f1 ():
 print (Business Unit 1 Data Interface ...)
# The Business unit did not start executing the F1 function
execution result:
authentication succeeded!
Business Unit 1 Data Interface
... Log Add success

Did you see that? I just defined the function, the business unit has not called the F1 function, the program has done all the work. That's why you encapsulate a layer of functions.

Iv. parameter transfer of the adorner

Careful friend may have found, in the example above, the F1 function has no parameters, in fact, it will need parameters, how to pass the parameters?

The case of a parameter:

def outer (func):
 def inner (username):
 print ("Certified successful!") Result
 = func (username)
 Print ("Log add succeeded") return result return
 inner
@outer
def F1 (name):
Print ("%s is connecting business Unit 1 Data Interface ..."%name)
# Calling method
F1 ("Jack")

In the definition of the inner function also add a parameter, call the Func function when passing this parameter, very good to understand? But the problem is again, then another department called the F2 has 2 parameters? F3 have 3 parameters? How do you pass it?

Very simple, we have *args and **kwargs! Known as "Universal parameters"! Simply modify the above code:

def outer (func):
 def Inner (*args,**kwargs):
 print ("Certified successful!") Result
 = func (*args,**kwargs)
 print ("Log add succeeded") return result return
 inner
@outer
def f1 (name,age):
 print ("%s is connecting business Unit 1 Data Interface ..."%name)
# Calling method
F1 ("Jack", 18)

Five, further thinking

Can a function be decorated with more than one function? OK! Look at the example below!

def outer1 (func):
 def Inner (*args,**kwargs):
 print ("Certified successful!") Result
 = func (*args,**kwargs)
 print ("Log add succeeded") return result return
 inner
def outer2 (func):
 def Inner (*args,**kwargs):
 print ("A welcome message ...") Result
 = func (*args,**kwargs)
 print ("A farewell message ...") ") return result return
 inner
 @outer1
@outer2
def F1 (name,age):
 print ("%s Connecting business Unit 1 Data Interface ... "%name"
# Call Method
F1 ("Jack",) 
execution Result:
authentication Successful!
a welcome message ...
Jack is connecting to business Unit 1 data Interface
... A farewell message ...
Log Add success

Further, can the adorner itself have parameters? OK! Look at the following example:

# certified function
def auth (request,kargs):
 print ("Certified success!") ")
# log function
def log (Request,kargs):
 print (" Log add Success ")
# Adorner function. Receives two parameters, which should be the name of a function.
def Filter (auth_func,log_func):
 # The first layer of encapsulation, the F1 function is actually passed to main_fuc this parameter
 def outer (main_func):
 # Second-tier encapsulation, The parameter values of the Auth and log functions are passed to this
 def wrapper (Request,kargs): # The logic of the
 following code is not important, it is important to refer to the parameter and return value
 Before_result = Auth (Request,kargs)
 if (Before_result!= None): return
 Before_result;
 Main_result = Main_func (Request,kargs)
 if (Main_result!= None): return
 Main_result;
 After_result = log (Request,kargs)
 if (After_result!= None): return
 After_result;
 Return wrapper return
 Outer
# Note that the adorner function here has parameters oh, it means that the filter function is executed first
# then returns the filter function as the name of the adorner function, so ,
# In fact, here, Filter (auth,log) = outer, @Filter (auth,log) = @outer
@Filter (auth,log)
def F1 (name,age):
 Print ("%s is connecting business Unit 1 Data Interface ..."%name)
# call Method
F1 ("Jack",)
Run Result:
authentication Successful!
Jack is connecting to business Unit 1 data Interface
... Log Add success

Are you dizzy again? In fact, you can understand this, first execute the filter function, get its return value outer, and then execute the @outer adorner syntax.

Above is the entire content of this article, I hope the content of this article for everyone's study or work can bring some help, but also hope that a lot of support cloud Habitat community!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.