current position:Home>Python core programming - decorator

Python core programming - decorator

2022-01-30 04:39:04 Alvin305

Little knowledge , Great challenge ! This article is participating in “ A programmer must have a little knowledge ” Creative activities

This article also participates in 「 Digging force Star Program 」, Win a creative gift bag , Challenge creation incentive fund

  Decorator

Decorator (Decorators) yes Python An important part of , In program development, we often use , Make good use of the decorator and get twice the result with half the effort . In short : It is a function that modifies the functions of other functions . It helps keep our code short , And more Pythonic(Python aron ).

python The decorator is essentially a function , It allows other functions to add extra functionality without any code changes , The return value of the decorator is also a function ( References to functions .

General understanding of decorators :

    The essence : It's a closure function

   Parameters : The function to decorate ( Is a function name, not a function call , That is, without parentheses )

   return : Is a decorated function ( It's also a function name, not a function call )

   effect : Add additional functionality to existing functions without any internal modifications

   characteristic : The internal code of the function to be decorated does not need any change

1. Start with an example

A company has an employee information management system , It mainly includes adding employee information , Update employee information , Delete employee information , View employee information and other functions , Anyone can access and operate . It took some time to find out that this is not working. We should limit it , Perform login verification before each operation , Only those who have logged in can operate , The original functions are as follows :

def add_staff():
    print(' Add employee information ')

def del_staff():
    print(" Delete employee information ")

def upd_staff():
    print(" Update employee information ")

def view_staff():
    print(" View employee information ")
 Copy code 

So the technical director handed the task to the small company at the same time A And small B2 personal .

Small A Is an intern who is going to graduate , Its implementation is to add verification logic to each function function , as follows :

def add_staff():
    # validate logon 
    print(' Add employee information ')

def del_staff():
    # validate logon 
    print(" Delete employee information ")

def upd_staff():
    # validate logon 
    print(" Update employee information ")

def view_staff():
    # validate logon 
    print(" View employee information ")
 Copy code 

Small B He is an engineer with some working experience , His plan is , Define an additional login verification function , Then call... Respectively in the function function , as follows :

def add_staff():
    check_login()
    print(' Add employee information ')

def del_staff():
    check_login()
    print(" Delete employee information ")

def upd_staff():
    check_login()
    print(" Update employee information ")

def view_staff() check_login():
    print(" View employee information ")

def check_login():
    # validate logon 
 Copy code 

After completing the task, the technical director looked at the completion of the next two people , But he didn't make any comments , But a new demand , Another permission verification is required , Operation can only be carried out after verification . So small A Small B Go back and continue to modify , Small A Still add permission verification logic to each function , Small B It is still defined as a permission verification function and then called in each function function. .

Obviously , Small B The implementation of is obviously better than that of small A Of , Imagine if there are dozens of such methods , According to little A Isn't the same code to operate in the same way copy Dozens of times , Once the logic changes, it will be modified dozens of times , It's very troublesome to maintain . And small B It's much better. , Just change the check function , Then you can call it in the function function. , The efficiency is greatly improved .

But after the second round of modification , The supervisor read it and still didn't give any comments , Continue to make another demand ( Demand is changing , As a programmer, you know ), The principle of openness and closeness is required , The internal code of the implemented function cannot be modified , But the function has to be extended . namely :

  • closed : The implemented function code cannot be modified
  • to open up : Add other functions without changing the internal code

It's a small one A It's completely masked , I don't know where to start , And small B It's also a silly face , So small A Small B Start surfing the Internet to find all kinds of information , Finally, after several times of painstaking inquiry, Xiao B Found the answer - - Decorator

The final modified code is as follows :

def check_login(fun):
    def inner():        
        # validate logon 
        fun()
    return inner

@check_login
def add_staff():
    print(' Add employee information ')

@check_login
def del_staff():
    print(" Delete employee information ")

@check_login
def upd_staff():
    print(" Update employee information ")

@check_login
def view_staff() print(" View employee information ")  Copy code 

The verification function before operation is completed without changing the internal logic , The supervisor nodded with satisfaction , Small B A raise is expected ...

3. Detailed explanation of ornaments

Alone with add_staff For example :python The interpreter interprets the code from top to bottom , Steps are as follows :

    1) Define a closure function check_login, That is, the function check_login Load into memory

    2)@check_login

    3) Definition add_staff function

On the surface, the interpreter will only interpret these sentences of code , Because the internal code of a function will not be executed before it is called . however @check_login There are many articles inside this code ,@ The function name is python A grammatical sugar in .

The above example @check_login The following operations will be performed internally :

perform check_login function

    call check_login function , And will @check_login The following function is used as check_login Parameters passed to check_login, namely @check_login Equivalent to check_login(add_staff), So it's done internally

def inner() # validate logon  fun() #fun Is the parameter passed in , At this time fun Equivalent to add_staff return inner  Copy code 

#fun Is the parameter passed in , At this time fun Equivalent to add_staff

# The return is inner,inner It's also a function , But what is returned is not the call of the function, but the reference of the function ( Or the address of the function ) return , In fact, the original add_staff Function into another function inner in , And call in this function .

So when you want to add employees in the future , The original logic is called , The difference is that there will be a login verification before each addition . In this way, verification is performed , The original function of adding employees is implemented again , In other words, the necessary extended functions are realized without changing the original logic .

4. Decoration sequence of decorators

Let's look at a list first

# Define a function : Complete package data 
def makeBold(fn):
    def wrapped():
        return '<b>' + fn() + '</b>'
    return wrapped

# Defined function , Complete the data package 
def makeItalic(fn):
    def wrapped():
        return '<i>' + fn() + '</i>'
    return wrapped

@makeBold
def test1():
    return 'hello world-1'

@makeItalic
def test2():
    return 'hello world-2'

@makeBold
@makeItalic
def test3():
    return 'hello world-3'

# Call function 
print(test1())
print(test2())
print(test3())

# Running results :

<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
 Copy code 

From the above example, we will find that ,test1 and test2 It's no different from what we said above , Other extensions are carried out before calling function functions , In this case, it is before returning a string , A package in which the string is first bold or italicized .

Next, let's focus on the third example , Here we are right test3 It was decorated twice, namely @makeBold and @makeItalic, Only from the execution order of the code, it should be carried out first makeBold Decorate and then makeItalic decorate , That is to say, call makeBold Package and then call with the returned result makeItalic Conduct < i> The parcel , Then the final result should be < i >< b >hello world-3</ b ></ i>. However, from the operation results , Contrary to what we thought , Actually, we did it first makeItalic After decoration makeBold decorate . This is the focus of this section .

namely : When multiple decorators decorate the same function at the same time ,python The interpreter interprets from top to bottom in order , But when decorating , It starts from the decorator closest to the function and decorates from bottom to top , This is why the results we imagine are different from the actual results .

After understanding the above paragraph , We're going back to the previous Liezi , First python The interpreter explains first @makeBold Then explain @makeItalic, Then, when decorating, first carry out makeItalic Decoration, and then carry on with the results of decoration makeBold decorate , So what we see is < b>< i>hello world-3</ i></ b> instead of < i>< b>hello world-3</ b></ i>

Let's use the code to understand it step by step :

# Define a function : Complete package data 
def makeBold(fn):
    def wrapped():
        return '<b>' + fn() + '</b>'
    return wrapped

# Defined function , Complete the data package 
def makeItalic(fn):
    def wrapped():
        return '<i>' + fn() + '</i>'
    return wrapped


@makeBold
@makeItalic
def test3():
    return 'hello world-3'

# The decoration steps are as follows : First of all, test3 Conduct makeItalic decorate 
@makeItalic
def test3():
    return 'hello world-3'

# The result after decoration can be understood as 
def test3():
    return '<i>hello world-3</i>'
# If there is only one decorator , At this point, it's over , But now multiple decorators are decorated at the same time , So in makeItalic After the decoration, there is a makeBold, So the next step is :
@makeBold
def test3():
    return '<i>hello world-3</i>'

# So finally, it goes through makeBold After decoration, the result is :'<b><i>hello world-3</i></b>'
 Copy code 

5. Decorated functions have parameters and return values

Sometimes we need to decorate functions with some parameters and return values , In this case, the treatment is also very simple , When defining the decorator , In the internal function of the closure function, you can declare that it has the same parameters as the decorated function , If there is a return value , Then return the decorated function .

Look at an example :

def outer(func):
    def inner(a,b):# The parameters declared here are consistent with the parameters of the decorated function 
        # Processing logic 
        return func()# Note that here is the call of the return function , If the decorated function does not return, You don't have to go back here 
    reutrn inner # What is returned here is the reference of the function 

@outer
def test(a,b):
    return a+b
 Copy code 

6. Function and summary of decorator

  1. Introducing logs

  2. Function execution time statistics

  3. Preprocessing before executing a function

  4. Clean up after function execution

  5. Permission check and other scenarios

  6. Caching mechanisms

summary : In general, in order to make decorators more versatile , You can define a decorator with an indefinite length parameter and a return value .

copyright notice
author[Alvin305],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/01/202201300438403034.html

Random recommended