current position:Home>It's better to know the python decorator than to know the heart of your girlfriend

It's better to know the python decorator than to know the heart of your girlfriend

2022-02-02 06:43:21 Charlie is not a dog

stay Python Inside , A function can be passed in as an argument , Functions can also be copied to variables , Call functions through variables . Decorators can extend the function of a function , Make a decorator comment for the function , The functions defined in the decorator can be executed in advance for all functions , Improve code reuse .

So now there is such a scene .

Clock in

There are all kinds of positions in Internet companies , Like programmers 、 The front desk ..., Before the programmer turns on the computer , You need to punch in , The front desk needs to open earlier ( In fact, I don't know who usually opens the door , Let's assume that the front desk opens the door ), You need to punch in before the front desk opens . in other words , Clocking is the first public action for all employees , So you can take the punch out function as a public logic .

Ordinary function call method

Naturally think of , It can be realized as follows .

def di(f):
    print('%s  Clock in , drop ...' % f.__name__)
    return f()
​
def boot():
    print(' Turn it on ')
​
def open():
    print(' Open door ')
​
if __name__ == '__main__':
    """
     Before the programmer starts up , Before the front desk opens , You need to punch in the fingerprint machine outside the door .
    """
    di(boot)
    di(open)
​
 Copy code 

Defines a function di(f), You can print f.__name__ namely f Function name information of , At the same time return to f() The results of the implementation of .

Be careful :__name__ If you import as a module ,module.__name__ It's the name of the module itself , If the module executes itself as a script , return __main__.

Execution results :

boot  Clock in , drop ...
 Turn it on 
open  Clock in , drop ...
 Open door 
 Copy code 

This design , If there are many functions to call , It's very troublesome. , Then decorators come in handy .

Simple decorator And @ Grammatical sugar

Decorator : How to dynamically add functions during code running , be called “ Decorator ”(Decorator).

Simple decorator

Define a di(f) Method , Or pass in the function of the logic to be executed as an argument , It defines a wrapper function , The return value is f The results of the implementation of . stay if __name__ == '__main__': Inside , This decorator was called , Do not modify the defined function , Add features dynamically during runtime " Clock in ".

import functools
​
#  Simple decorator 
def di(f):
    """
     Before the programmer starts up , Before the front desk opens , You need to punch in the fingerprint machine outside the door .
    :param f:  Pass in a function 
    :return:
    """
    #  Put the original function of __name__ Wait for attributes to be copied to wrapper()
    @functools.wraps(f)
    def wrapper():
        print('%s  Clock in , drop ...' % f.__name__)
        return f()
    return wrapper
​
def boot():
    print(' Turn it on ')
​
def open():
    print(' Open door ')
​
if __name__ == '__main__':
​
    #  The first one is , Simple decorator 
    a = di(boot)
    a1 = di(open)
    print(a.__name__) #  result wrapper  Add @functools.wraps(f) The result is  boot
    a()
    a1()
 Copy code 

di(boot) The return value of a Namely wrapper function , adopt a() It's called wrapper function , obtain boot The return value of . Empathy ,di(open) equally .

result

boot
boot  Clock in , drop ...
 Turn it on 
open  Clock in , drop ...
 Open door 
 Copy code 

because di(boot) The return value of a Namely wrapper function , that print(a.__name__) The result is, of course, that wrapper, We hope to be boot, What do I do ,functools.wraps(f) This annotation can put the original function boot Of __name__ Wait for attributes to be copied to wrapper(), This line of code comments can also run , that print(a.__name__) The result is that wrapper.

The second kind ,@ Grammatical sugar

adopt @ Grammatical sugar , You can also apply decorators to functions , recommend .

import functools
​
def di(f):
    """
     Before the programmer starts up , Before the front desk opens , You need to punch in the fingerprint machine outside the door .
    :param f:  Pass in a function 
    :return:
    """
    #  Put the original function of __name__ Wait for attributes to be copied to wrapper()
    @functools.wraps(f)
    def wrapper():
        print('%s  Clock in , drop ...' % f.__name__)
        return f()
    return wrapper
​
# @  Grammatical sugar 
@di
def boot2():
    print(' Turn it on ')
​
@di
def open2():
    print(' Open door ')
​
if __name__ == '__main__':
​
    #  The second kind ,@  Grammatical sugar 
    boot2()
    open2()
 Copy code 

@di The mark is equivalent to ,a2 = di(boot2) a2(). Don't bother , Because of the addition of @ A symbol mark , Direct use boot2() Call the decorator .

result

boot2  Clock in , drop ...
 Turn it on 
open2  Clock in , drop ...
 Open door 
 Copy code 

Business logic functions require parameters

Business logic functions may require parameters , such as :

def boot(name):
    print('%s  Turn it on ' % name)
 Copy code 

that , Just change the front decorator to :

import functools
​
#  Business logic functions require parameters 
def di(f):
    """
     Before the programmer starts up , Before the front desk opens , You need to punch in the fingerprint machine outside the door .
    :param f:  Pass in a function 
    :return:
    """
    #  Put the original function of __name__ Wait for attributes to be copied to wrapper()
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print('%s  Clock in , drop ...' % f.__name__)
        return f(*args, **kwargs)
    return wrapper
​
@di
def boot(name):
    print('%s  Turn it on ' % name)
​
if __name__ == '__main__':
    boot('keguang')
 Copy code 

result :

boot  Clock in , drop ...
keguang  Turn it on 
 Copy code 

to wrapper Also add *args, **kwargs Parameters , stay boot It calls f(*args, **kwargs) that will do . By the way :

  • *args: An array can be passed in
  • **kwargs: You can pass in a k-v For parameters

The sequence corresponds to , Array parameter before . give an example :

def f(*args, **kwargs):
    print('args=', args)
    print('kwargs=', kwargs)
​
print(f(1, 2, 3, a = 'a', b = 'b'))
​
#  result 
# args= (1, 2, 3)
# kwargs= {'a': 'a', 'b': 'b'}
 Copy code 

Parameter decorator

If the decorator also has parameters , For example, if an employee comes to work early in the morning < 9:00, I can give you a compliment , So it's equivalent to just need the front di() There's a layer of functions ,di_args that will do , stay wrapper Inside . Use this parameter

import functools
​
#  Parameter decorator 
def di_args(time):
    def di(f):
        """
         Before the programmer starts up , Before the front desk opens , You need to punch in the fingerprint machine outside the door .
        :param f:  Pass in a function 
        :return:
        """
        #  Put the original function of __name__ Wait for attributes to be copied to wrapper()
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            if time < '9:00':
                print(' It's early , great ...')
​
            print('%s  Clock in , drop ...' % f.__name__)
            return f(*args, **kwargs)
        return wrapper
    return di
​
@di_args('8:00')
def boot(name):
    print('%s  Turn it on ' % name)
​
if __name__ == '__main__':
    boot('keguang')
 Copy code 

Parameter in @di_args('8:00') Just pass it in , It's kind of like java The notes inside . Finally, it's through boot('keguang') Call , result :

 It's early , great ...
boot  Clock in , drop ...
keguang  Turn it on 
 Copy code 

Class decorator

Class decorators rely mainly on class __call__ Method , When using @ Form to attach a decorator to a function , This method will be called .

#  Class decorator 
class di(object):
    def __init__(self, f):
        self._f = f
​
    def __call__(self, *args, **kwargs):
        print('decorator start...')
        self._f()
        print('decorator end...')
​
@di
def boot():
    print(' Turn it on ')
​
if __name__ == '__main__':
    boot()
 Copy code 

add @di Decorator logo , Will use boot To instantiate di class , And then execute __call__ function ,object Indicates that this class can pass in any type of parameter . Running results

decorator start...
 Turn it on 
decorator end...
 Copy code 

A typical application scenario of decorators is playing log journal , If all logic needs to log the program's health , So we can deal with these logic ( function ) Add log module decorator , We can achieve the corresponding purpose .

Last

If the article is useful to you , Give the author a little praise
No white whoring , Starts from me

copyright notice
author[Charlie is not a dog],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/02/202202020643195663.html

Random recommended