0. What is Python decorator?

What is a decorator? What is a decorator? Let’s take an example: the evolution of decorators, where all programs are iterated step by step, optimized from redundant structures and iterated to final modular code. Watching from the beginning will give you a thorough understanding of the evolution of Python decorators, the order in which they are executed, and the order in which multiple decorators are executed.

#1. Define a function that extends its functionality without modifying the function code. Such as permission validation.
def f1() :
    print("Here's how the F1 function works.")
    
#2. Define a high-level function (closure) to implement permission validation for the f1() function.
def fn(f1) :
    def fc() :
        print("Here we start to verify the f1 function permission.")
        f1()
        print("The f1 function is done.")
    return fc


#3. Implementation: For function calls, the implementation of f1() function call permission verification.
t = fn(f1)
t() #t() is equivalent to fn(f1)(). It calls the function fc() inside fn(f1)
The result is as follows: here is the permission verification for f1 function. Here is the function of f1 function.

Fn (f1)(); fn(f1)(); fn(f1)(); The structure is bloated.For the sake of visualization and modularization, the advanced decoration functions of the same function above are uniformly identified to achieve better results.def fn(f1) :
    def fc() :
        print("Here we start to verify the f1 function permission.")
        f1()
        print("The f1 function is done.")
    return fc
@fn  # This @fn identifier has the same effect as f1=fn(f1).
def f1() :
    print("Here's how the F1 function works."Note that fn(f1)() is not called by fn(f1)(), but by f1(). Since @fn has the same effect as f1=fn(f1), calling f1() directly implements fn(f1)() to achieve the original effect. Same effect but much simpler code. f1()The result is as follows: here is the permission verification for f1 function. Here is the function of f1 function.
Copy the code

From the above example, we can conclude that:

1. A decorator is essentially a high-level Python function that decorates functions by adding an @ symbol to other functions

2. The function of decorator: It allows other functions to add extra functions without making any code changes. The return value of decorator is also a function object. It is often used in scenarios with faceted requirements, such as logging insertion, performance testing, transaction processing, caching, and permission verification. Decorators are a great way to solve this problem, because with decorators we can strip away a lot of code that has nothing to do with the function itself and continue to reuse it.

3. Interview question: What is a Python decorator? A Python decorator is a function used to extend the functionality of an original function. This function is special in that its return value is also a function. The advantage of using a Python decorator is to add new functionality to the function without changing the code of the original function.

1. The use of a single decorator: the execution principle of decorators

#1. Define two decorators (closure functions, decorator functions can be called either) that give bold and slanted labels to fonts.
def makeBold(fn) :
    print("BBBBB"*5)
    def wrapped() :
        print("bbbbb"*5)
        return "<b>" + fn() + "</b>"
    return wrapped

def makeItalic(fn) :
    print("IIIII"*5)
    def wrapped() :
        print("iiiiii" * 5)
        return "<i>" + fn() + "</i>"
    return wrapped

#2. The use of decorator, directly @ add the form of function name, on the need to decorate the function head can be.
@makeBold  The # effect is the same as test_Bold=makeBold(test_Bold). The decorator is placed on a function, which is passed to the decorator as an argument
def test_Bold() :
    print("test_Bold"*5)
    return "this is the test_Bold"

@makeItalic The effect is the same as that of test_Italic=makeItalic(test_Italic). Placing a decorator on a function is equivalent to passing the function as an argument to the decorator function
def test_Italic() :
    print("test_Itali" * 5)
    return "this is the test_Italic"
Copy the code

The following implementation tests the individual decorator code above:

1.1 Directly run the program above, what do not call, also do not operate, discover also have a result.

"' the results are as follows: BBBBBBBBBBBBBBBBBBBBBBBBB IIIIIIIIIIIIIIIIIIIIIIIII ' ' '
Copy the code

Cause analysis:

Execute the Python program without calling any methods, and find that the first print function in the makeBold and makeItalic functions is also executed. Because @makebold has the same effect as test_Bold=makeBold(test_Bold), when the program executes, loading the identifier @makeBold@makeItalic above the function body from top to bottom, Test_Bold =makeBold(test_Bold),test_Italic=makeBold(test_Italic), so makeBold and makeItlic, So the first print statement of the two functions is executed in turn. Because @makebold comes first, the result is BBBB…. above And IIIIIII… (The two functions also return values, which are internal closures of the two functions, but are not shown here.)

Summary Point 1:

In Python, decorators are automatically loaded as the program is loaded, regardless of whether a method is called or not. So anything outside the decorator’s internal functions will be automatically loaded and executed without being called.

1.2. Call the function decorated by the decorator

#1. Define two decorators (closure functions, decorator functions can be called either) that give bold and slanted labels to fonts.
def makeBold(fn) :
    print("BBBBB"*5)
    def wrapped() :
        print("bbbbb"*5)
        return "<b>" + fn() + "</b>"
    return wrapped
 
def makeItalic(fn) :
    print("IIIII"*5)
    def wrapped() :
        print("iiiiii" * 5)
        return "<i>" + fn() + "</i>"
    return wrapped
 
#2. The use of decorator, directly @ add the form of function name, on the need to decorate the function head can be.
@makeBold  The # effect is the same as test_Bold=makeBold(test_Bold). The decorator is placed on a function, which is passed to the decorator as an argument
def test_Bold() :
    print("test_Bold"*5)
    return "this is the test_Bold"
 
@makeItalic The effect is the same as that of test_Italic=makeItalic(test_Italic). Placing a decorator on a function is equivalent to passing the function as an argument to the decorator function
def test_Italic() :
    print("test_Itali" * 5)
    return "this is the test_Italic"

The function is called ----------------- under #-----------
t = test_Bold()   Call test_Bold(), equivalent to: makeBold(test_Bold)()
print(t)  Print the return value of test_Bold
The results are as follows:  BBBBBBBBBBBBBBBBBBBBBBBBB IIIIIIIIIIIIIIIIIIIIIIIII bbbbbbbbbbbbbbbbbbbbbbbbb test_Boldtest_Boldtest_Boldtest_Boldtest_Bold this is the test_Bold '''
Copy the code

Cause analysis:

  1. First of all, after the execution of the program, loading from top to bottom, must be loaded to @makeBold@makeItalic first, the same principle, at this time the first BBBBBB…. And IIIIIIII… It’s printed.
  2. Since @makebold has the same effect as test_Bold=makeBold(test_Bold), the program is printing BBBB…. After that, the return statement: return wrapped; Wrapped is a function reference, so the result is equivalent to test_Bold=wrapped. Test_Bold points to the closure function wrapped. When t=test_Bold() is executed, it is equivalent to t=test_Bold()=wrapped(). So the wrapped function is executed at this time. ****print(” BBBBB “*5) Print the BBBBBBBBBBBBBBBBBBBBBBBBB. Similarly, the return value of @makeitalic’s test_Italic function is also a wrapped reference, but wrapped is not executed internally because there is no subsequent call to wrapped. Here are the difficult, difficult, difficult)
  3. Execute the wrapped return statement below, because test_Bold() is called in the return statement. Test_Bold () is used to execute the test_Bold() function. print(“test_Bold”*5) “Statement, printing test_Boldtest_Boldtest……..
  4. Test_Bold () returns this is the test_Bold, as an argument to the wrapped return “<b>” + fn() + “</b>”.
  5. So the wrapped function returns: <b>this is the test_Bold</b>, and assigns the return value to t and prints t. So the result of the entire function call statement looks like this.

Summary Point 2:

1. Decorators are loaded with the execution of the program, and are automatically loaded without calling the function.

2. Decorator principle: putting @ decorator name (@makebold) on the head of a function is equivalent to passing the function as a parameter to the decorator function to execute, which is equivalent to test_Bold=makeBold(test_Bold). The use of decorator greatly simplifies the code of the program.

2. Multiple decorators decorate functions simultaneously: decorator execution order

#1. Define two decorator functions to give the font bold and slanted labels respectively.
def makeBold(fn) :
    print("BBBBB"*5)
    def wrapped1() :   Wrapped functions wrapped1 and wrapped2
        print("bbbbb"*5)
        return "<b>" + fn() + "</b>"
    return wrapped1

def makeItalic(fn) :
    print("IIIII"*5)
    def wrapped2() :     Wrapped functions wrapped1 and wrapped2
        print("iiiiii" *3)
        return "<i>" + fn() + "</i>"
    return wrapped2

#2. Use two decorators to decorate a function at the same time. The principle of the same
@makeBold   Test_B_I =makeBold(makeItalic(test_B_I))
@makeItalic Test_B_I =makeItalic(test_B_I)
def test_B_I() :   
    print("test_B_I"*5)
    return "this is the test_B_I"
Copy the code

The following implementation tests the above two decorator code:

2.1 Directly run the program above, what do not call, do not operate, discover also have a result

The results are as follows: Note III.... And BBBB... The order of the IIIIIIIIIIIIIIIIIIIIIIIII BBBBBBBBBBBBBBBBBBBBBBBBB "'
Copy the code

Cause analysis:

1. Notice that @makebold is written above @makeitalic, but the result shows that @makeitalic is executed first, that is, the makeItalic function is loaded first. So when a function is decorated with multiple decorators, the decorators are loaded from the inside out. It’s easy to understand: decorators are for functions, so load them from the inside out, starting with the decorator next to the function. So: the printed result is IIIIIII…… And BBBBB…

 @makeBold  Test_B_I = makeBold(makeItalic(test_B_I))
 @makeItalic Test_B_I = makeItalic(test_B_I)
 def test_B_I() :
       print("test_B_I"*5)
       return "this is the test_B_I"
Copy the code

2.2. Call a function decorated with two decorators

#1. Define two decorator functions to give the font bold and slanted labels respectively.
def makeBold(fn) :
    print("BBBBB"*5)
    def wrapped1() :   Wrapped functions wrapped1 and wrapped2
        print("bbbbb"*5)
        return "<b>" + fn() + "</b>"
    return wrapped1
 
def makeItalic(fn) :
    print("IIIII"*5)
    def wrapped2() :     Wrapped functions wrapped1 and wrapped2
        print("iiiiii" *3)
        return "<i>" + fn() + "</i>"
    return wrapped2
 
#2. Use two decorators to decorate a function at the same time. The principle of the same
@makeBold   Test_B_I =makeBold(makeItalic(test_B_I))
@makeItalic Test_B_I =makeItalic(test_B_I)
def test_B_I() :   
    print("test_B_I"*5)
    return "this is the test_B_I"

#----- Note the following call to -------------------- for the function decorated with two decorators
test_B_I()  # call test_B_I() with two decorators
print(test_B_I()) Print the return value of test_B_I
The results are as follows:  IIIIIIIIIIIIIIIIIIIIIIIII BBBBBBBBBBBBBBBBBBBBBBBBB bbbbbbbbbbbbbbbbbbbbbbbbb iiiiiiiiiiiiiiiiii test_B_Itest_B_Itest_B_Itest_B_Itest_B_I this is the test_B_I '''
Copy the code

Cause analysis:

  1. 1. In the same way above, since decorators are for functions, when a function is decorated with multiple decorators, the decorators are loaded from the inside out and from the bottom up, so: the printed result is IIIIIII…… And BBBBB…  
  2. @makeBoldTest_B_I = makeBold(makeItalic(test_B_I))
     @makeItalic Test_B_I = makeItalic(test_B_I)
     def test_B_I() :
           print("test_B_I"*5)
           return "this is the test_B_I"
    Copy the code
  3. Notice in the code above that for multiple decorators, the decorator is loaded from the inside out. So first @makeitalic decorates test_B_I, The effect is the same as test_B_I = makeItalic(test_B_I). The return value is its internal wrapped2 function reference. The result of @makeitalic load execution: test_B_I=wrapped2. The result (function) is then decorated by the @Makebold decorator. So the @makebold decorator is equivalent to: test_B_I=makeBold(makeItalic(test_B_I)), or test_B_I=makeBold(wrapped2). Because makeBold() returns wrapped1. Namely makeBold (wrapped2) = wrapped1. So the final @makeBold@makeItalic decorates the result to test_B_I=wrapped1. Just call wrapped2 from wrappeD1. Therefore, when the function call test_B_I() is executed, it is equivalent to executing wrapped1(). Print this time wrapped1 function within the print (” BBBBB “* 5), so then results: BBBBBBBBBBBBBBBBBBBBBBBBB
  4. Next, execute the wrapped1 function return statement: return “” + fn() + ““. Because the effect of the @makebold decoration is equivalent to makeBold(makeItalic(test_B_I)), that is, the function of the makeBold decoration is the result of the @makeitalic decoration. Equivalent to makeBold(wrapped2). Return “” +wrapped2() + “/b>” So the wrapped2 function is called at this point. Print (” iiiIII “*3). So it prints out: iiIIIIIIiiiiIIIIIIIIIIIII
  5. Then call wrapped2 and return “< I >” + fn() + ”
    “. Since wrapped2 decorates the test_B_I function, here fn()=test_B_I(). Print (“test_B_I”*5). Print (“test_B_I”*5) It printed: test_b_ITEST_b_ITest_b_itest_b_itEST_b_itEST_b_i
  6. The return value for test_B_I is this is the test_B_I. So wrapped2 returns < I >this is the test_B_I</ I >. So wrapped1 returns: <b>< I > This is the test_B_I</ I ></b>. So the return value of test_B_I(), which is finally decorated by two decorators, is: <b>< I > This is the test_B_I</ I ></b>. So the result of the whole function call is as follows.

To summarize the main points about multiple decorators modifying a function:

1.When a function is decorated with multiple decorators, the decorators are loaded from the inside out (from the bottom up). It’s easy to understand: decorators are for functions, so load them from the inside out, starting with the decorator next to the function

2. Decoration of the outer layer is to decorate the result of the decoration of the inner layer. The function equivalent to the outer decorator decoration is the result function of the inner decorator decoration function (the decorated return function).

Unified statement: About the original blog content, there may be some content reference from the Internet, if there is an original link will be quoted; If can not find the original link, in this statement if there is infringement please contact to delete ha. About reprint blog, if have original link will declare; If can not find the original link, in this statement if there is infringement please contact to delete ha.