Detailed Python Decorator

Source: Internet
Author: User
Tags python decorator
The function of adorners in many languages, the name is not the same, in fact, it embodies a design pattern, emphasizing the open and closed principle, more for the post-function upgrade instead of writing new code. Decorators can not only decorate functions, but also decorate other objects, such as classes, but usually we use decorative functions as an example to illustrate their usage. To understand the principle of adorners in Python, it takes a step-by-step. This article is as easy to describe as possible, from the most basic content.

(Note: The following uses the 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 definition code block of the function does not execute it immediately until the function is called to execute its inner block of code.

def foo ():p rint ("foo function is running! If this is the case, the statement in Foo will not be executed. The program simply reads the definition code block into memory.

Look again, example of sequential execution:

def foo (): Print ("I am the function definition above!") def foo (): Print ("I am the function definition below!") ") foo () Run result: I am the following function definition

It can be seen that because of the sequential execution, the following Foo overwrites the above foo. Therefore, the placement of code in Python is required and cannot be placed arbitrarily, and the function body is placed before the called statement.

Second, we have to figure out a few things: function name, function body, return value, function memory address, function name parentheses, functions name is treated as argument, function name parentheses are treated as parameters, return function name, return function name parentheses. For the following functions:

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

Function Name: foo

function Body: 第1-3 line

return value: string "OK" if the return object is not explicitly given, the default return is None

memory address of the function: when the function body is read into memory, it is referenced by the identifier, the function name Foo,
This means that Foo points to where the function body is stored in memory.

Function name parentheses: for example, foo (), function call method, only see this parenthesis, the program will be based on
The function name finds the function body 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 innerdef 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 in parentheses as the return value of the other function. In the above code, outer and Foo are two functions, and outer (foo) means that the function name of the Foo function is passed as an argument to the outer function and the outer function is executed; outer (foo ()) Indicates that the return value after the Foo function is passed as a parameter to the outer function and executes the outer function, since the Foo function does not specify a return value, it is actually passed to the outer function a none. Note the difference, there is no parenthesis is the key!

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

Keep in mind that function names, function parentheses can be passed as arguments or return values, and there are no brackets that are two distinct meanings!

Second, the use of decorative device scene

Adorners are typically used to add additional functionality to the existing function code and functionality without changing it. For example, before the original function executes the point of what, after the execution of the point what.

Let's take a look at an example of the adorner's usage scene and the design pattern embodied. (I'm sorry I can't design a better scene, only to cite the case of Wuhan University God to deduce)

There is a large company, the underlying platform department responsible for the development of internal applications and APIs, with hundreds of business units responsible for different businesses, each invoking the different functions provided by the base Platform department to handle their own business, as follows:

# Basic Platform Department has developed hundreds of functions 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 of the company's inception, the basic Platform Department to develop these functions, due to various reasons, such as time, such as ill-conceived and so on, there is no security authentication for function calls. Now, the director of the platform decided to compensate for the flaw, so:

The first time, the supervisor called an OPS engineer, the engineer ran up and down the department to inform, let them add the authentication function in the code, however, the day he was expelled.

The second time: The supervisor also called a OPS engineer, the engineer used Shell to write a complex script, reluctantly realized the function. But he'll be back in a few short time. The operation is not the development of good luck dimension ....

Third: The supervisor called a Python automation development engineer, and the guy did it: refactoring only the underlying platform's code, so that N business units don't have 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 the Certification Program code print ("Business Unit 2 Data Interface ...") def f3 (): # Add the Certification Program code print ("Business unit 3 Data Interface ...) def f100 (): #加入认证程序代码 print ("Business Unit 100 Data Interface ...") #各部门分别调用f1 () F2 () F3 () F100 ()

Four: The supervisor has another engineer, he did it: Define an authentication function, the original other function calls it, the code is as follows box. However, the supervisor was still dissatisfied, but this time he explained why. The director said: Write code to follow the open-closed principle, although in this principle is mainly for object-oriented development, but also for functional programming, it is simple, it stipulates that the function code has been implemented inside is not allowed to be modified, but the external can be extended, that is: Closed: implemented function code block; Open: open to extensions. If the open closure principle is applied to the above requirements, code modifications are not allowed inside the function F1, F2, f3......f100. Unfortunately, the engineer did not have a beautiful girlfriend, so he was soon expelled.

def login (): Print ("Authentication 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 times: There is no time for the supervisor to find someone else to do the job, he decided to go in person, and intends to add a log function after the function execution. The supervisor is like this: not the director of the decorator is not a good yard farm! Why do I have to be a supervisor and you can only be taken care of? Hey. His code is as follows:

#/usr/bin/env python#coding:utf-8def Outer (func): Def inner (): Print ("Authentication successful!") result = Func () print ("Log added successfully") return result return inner@outerdef F1 (): Print ("Business Unit 1 Data Interface ...") @outerdef F2 (): Print ("Business Unit 2 Data Interface ...") @outerdef F3 (): Print ("Business Unit 3 Data Interface ...") @outerdef F100 (): Print ("Business Unit 100 Data Interface ...") #各部门分别调用f1 ( ) F2 () F3 () F100 ()

For the above code, it is also necessary to expand the underlying platform code, you can implement the other departments call the function F1 F2 F3 F100 before the authentication operation, after the end of the operation to save the log, and other business units do not need their own code to make any changes, the calling method does not change. "Supervisor" after writing the code, feel alone happy than all Lele, intend to show off, so wrote a blog will be the process of detailed description.

Third, the interior principle of the adorner,

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

def outer (func): Def inner (): Print ("Authentication successful!") result = Func () print ("Log added successfully") return result return inner@outerdef 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 top to bottom, reads def outer (func): When it finds out that this is a "first class citizen" function, it loads the function body into memory and then goes through.

When I read the @outer, the program was attracted by the syntax sugar, knowing that it was an adorner that was executed immediately, so the program started running the function defined by the name outer at the end of the @. (It is believed that no one will be foolish to write @outer to another location, it can only be placed at the top of the decorated function, not blank line.) )

The program returns to the outer function, starting to execute the adorner's grammar rules, which are dead, is Python's "law" and don't ask why. The rule is that the name of the decorated function is passed as a parameter to the adornment function. After the adornment function executes its own internal code, it assigns its return value to the decorated function.

As shown in the following:

It is important to note that:

@outer and @outer () have a difference, without parentheses, the outer function will still be executed, which is different from the traditional parentheses in order to call the function, need special attention! What about the brackets? That's the high-level use of adorners, which will be introduced later.

It is F1 that the function name (and not F1 () is called after the argument is passed to the adornment function outer, namely: Func = f1, @outer equals outer (F1), actually passes F1 's function body instead of the return value after F1.

The outer function returns the return value of the inner function name, not the inner ().

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

4. The program starts to execute the content inside the outer function, and at first it encounters a function that is very much around it, right? Of course, you can arrange the code before and after the inner function, but they're not the point, and it's a bit of a hassle, as explained below. The inner function definition block is not executed immediately after being observed by the program, but is read into memory (this is the unspoken rule).

5. Further down, hit return inner, the return value is a function name, and the function name will be assigned to F1 the 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 (in fact F1 the function name changes exponentially to inner the function name points to the function body address, F1 no longer points to its original function body memory address), The code inside the inner function is executed when the F1 is called back, not the previous function body. So where did the previous function body go? Remember when we passed F1 as parameters to the func parameter? Func This variable holds the old function in the memory of the address, through which can execute the old function body, you can see in the inner function of result = func () This code, it is so dry!

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

7. After the above process has gone, you should have seen, in the Business unit code and interface calls do not make any changes at the same time, there is no basic platform department of the original code to do internal modification, just add a decorative function, the implementation of our requirements, before the function call authentication, after the call to write the log. This is the biggest function of the adorner.

Question: So why do we have to make a outer function a inner function is so complicated? Isn't a layer of function OK?

A: Please note that @outer this code will automatically execute the code inside the outer function when the program is executed here, and if you do not encapsulate it, do something when the business unit has not made a call, which is somewhat inconsistent with the original intention. Of course, if you have a need for this, it's not going to work. Consider the following example, which has only one layer of functions.

def outer (func): Print ("Authentication successful!") result = Func () print ("Log added successfully") return Result@outerdef F1 (): Print ("Business Unit 1 Data Interface ...") # The business unit did not start executing the results of the F1 function: Successful certification! Business Unit 1 Data Interface ... Log added successfully

Did you see that? I just defined the function, and the business unit hasn't called the F1 function yet, and the program has done all the work. This is the reason for encapsulating a layer of functions.

Four, the parameter transfer of the adorner

Careful friend may have found, the above example, the F1 function does not have parameters, in the actual situation will definitely need parameters, that parameter how to pass it?

The case of a parameter:

def outer (func): Def inner (username): Print ("Authentication successful!") result = Func (username) print ("Log added successfully") return result return inner@outerdef F1 (name):p rint ("%s is connecting to Business Unit 1 Data Interface ...") Name) # Call Method F1 ("Jack")

A parameter is also added to the definition part of the inner function to pass this parameter when invoking the Func function, well understood? But then again, the other department calls the F2 with 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 ("Authentication successful!") result = Func (*args,**kwargs) print ("Log added successfully") return result return inner@outerdef F1 (name,age): Print ("%s is connecting business Unit 1 data Port ... "%name) # Call Method F1 (" Jack ", 18)

Five, further thinking

Can a function be decorated by multiple functions? OK! Look at the following example!

def outer1 (func): Def inner (*args,**kwargs): Print ("Authentication successful!") result = Func (*args,**kwargs) print ("Log added successfully") return result return innerdef Outer2 (func): Def inner (*args,**kwargs): PR Int ("A welcome message ... result = Func (*args,**kwargs) print ("A farewell message ... Return result return inner @outer1 @outer2def F1 (name,age): Print ("%s is connecting to Business Unit 1 Data Interface ..."%name) # Call Method F1 ("Jack", 18) Execution Result: Certification successful! A welcome message ... Jack is connecting the Business Unit 1 Data Interface ... A farewell message ... Log added successfully

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

# Authentication Function def auth (Request,kargs): Print ("Authentication successful!") ") # Log function Def log (Request,kargs): Print (" Log added successfully ") # Adorner function. Receives two parameters, both of 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): # The second layer of encapsulation, the Auth and the log function parameter values are passed here def wrapper (Request,kargs): # The following code's judgment logic is not important, it is important that the parameter reference 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 to first execute the filter function # and then return the filter function's return value as the name of the adorner function here, so, # actually here, Filter (auth , log) = outer, @Filter (auth,log) = @outer @filter (auth,log) def F1 (name,age): Print ("%s is connecting to Business Unit 1 Data Interface ..."%name) # Call method F1 ("Jack", 18) running result: Authentication successful! Jack is connecting the Business Unit 1 Data Interface ... Log added successfully

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

The above is the whole content of this article, I hope that the content of this article on everyone's study or work can bring some help, but also hope to support!

Related Article

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: 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.