SoFunction
Updated on 2024-10-30

python decorator principle source code example analysis

preamble

Someone recently asked me what a decorator is, so I told him that it's really a decorator is something like a girl's hairpin. A girl you like, she can have many hairpins, and when she wears different hairpins, her head is decorated with different hairpins. But your favorite girl is still your favorite girl. If you still feel do not understand, the decorator is our cell phone shell, you despite the set on the phone shell, but does not affect the function of your phone, but your phone or the phone can be given to you to play, the phone call call, the game to play the game, the collection of the siege lion white jade's blog on the collection of the siege lion white jade's blog. And your cell phone becomes a cell phone with a case.

Decorators are one of the roadblocks of python, you do or you don't do it, it's there. If you want to learn advanced python usage, the decorator is the tiger that you, Wusong, must defeat!

The setting of this paper is as follows:

win10

python3.7

I. What is a decorator

Decorator is to add new small functions to the existing module, you can extend the functionality of the original function, and also do not need to modify the content of the original function, and do not need to modify the original function call.

The use of decorators is consistent with the open-closed principle of object-oriented programming.

The principle of openness and closure is reflected in two main areas:

Being open to extensions means that when there are new requirements or changes, existing code can be extended to accommodate the new situation.

Being closed to modification means that once a class is designed, it can work independently without any modifications to the class.

II. Why use decorators

Before using decorators, we need to know that everything is actually an object in python, which means that everything can be passed as a parameter.

Functions can also be passed as arguments to functions.

You can visualize how a function name can be passed directly as a parameter by following this simple example

def baiyu():
    print("I'm White Jade, the Attack Lion.") 
def blog(name):
    print('Enter the blog function')
    name()
    print('My blog is /zhh763984017') 
if __name__ == '__main__':
    func = baiyu  # This assigns the function name baiyu to the variable func.
    func()  # Execute the func function
    print('------------')
    blog(baiyu)  # particle marking the following noun as a direct objectbaiyuThis function is passed as an argument to theblogfunction (math.)

The results of the implementation are shown below:

Next, I want to know thisbaiyurespond in singingblogWhat is the execution time for each of the two functions, so I modified the code as follows:

import time 
def baiyu():
    t1 = ()
    print("I'm White Jade, the Attack Lion.")
    (2)
    print("Execution time is:", () - t1) 
def blog(name):
    t1 = ()
    print('Enter the blog function')
    name()
    print('My blog is /zhh763984017')
    print("Execution time is:", () - t1) 
if __name__ == '__main__':
    func = baiyu  # This assigns the function name baiyu to the variable func.
    func()  # Execute the func function
    print('------------')
    blog(baiyu)  # particle marking the following noun as a direct objectbaiyuThis function is passed as an argument to theblogfunction (math.)

 

The above rewrite has achieved the functionality I need, however, when I have another new function [python_blog_list] as follows:

def python_blog_list():
    print('''[Python] Crawler battle, zero-basic first try crawler download images (with source code and analysis process)
    /zhh763984017/article/details/119063252 ''')
    print('''[Python] In addition to multithreading and multiprocessing, you also need to know about concurrency
    /zhh763984017/article/details/118958668 ''')
    print('''[Python] Crawler speed up tips, multi-threading and multi-process (with source code examples)
    /zhh763984017/article/details/118773313 ''')
    print('''[Python] Crawler parsing tool Xpath, from shallow to deep quickly mastered (with source code examples)
    /zhh763984017/article/details/118634945 ''')

Also need to calculate the function execution time of the, that according to the previous logic, is rewritten as follows:

def python_blog_list():
    t1 = ()
    print('''[Python] Crawler battle, zero-basic first try crawler download images (with source code and analysis process)
    /zhh763984017/article/details/119063252 ''')
    print('''[Python] In addition to multithreading and multiprocessing, you also need to know about concurrency
    /zhh763984017/article/details/118958668 ''')
    print('''[Python] Crawler speed up tips, multi-threading and multi-process (with source code examples)
    /zhh763984017/article/details/118773313 ''')
    print('''[Python] Crawler parsing tool Xpath, from shallow to deep quickly mastered (with source code examples)
    /zhh763984017/article/details/118634945 ''')
    print("Execution time is:", () - t1)

Wouldn't it be duplicating the wheel building if it was to be written like this too? Granted, humans are pigeonholes and repeaters by nature, but surely a good cv taker (ctrl+c and ctrl+v) would have to find a way to be lazy ah

 

Decorators, that is, allow us to extend some of the functionality that the original function does not have.

III. Simple Decorators

Based on the function execution time requirement above, we'll hand-write a simple decorator to implement it.

import time 
def baiyu():
    print("I'm White Jade, the Attack Lion.")
    (2) 
def count_time(func):
    def wrapper():
        t1 = ()
        func()
        print("Execution time is:", () - t1) 
    return wrapper 
if __name__ == '__main__':
    baiyu = count_time(baiyu)  # Because the decorator count_time(baiyu) returns the function object wrapper, this statement is equivalent to baiyu = wrapper.
    baiyu()  # fulfillmentbaiyu()就相当于fulfillmentwrapper()

Here count_time is a decorator, decorator function inside the definition of a wrapper function, the func function as a parameter into the function to achieve the function is to wrap the func, and return to the wrapper function. wrapper function body is to implement the contents of the decorator.

Of course, the wrapper function name here can be customized, as long as the function name you define is the same as the function name you return

IV. Syntactic Sugar for Decorators

If you've looked at the code inside other python projects, you'll inevitably see the @ symbol, which is the syntactic sugar for the decorator. So the simple decorator above can still be implemented with syntactic sugar, which eliminates the need for the

baiyu = count_time(baiyu)

This line of code instead calls the baiyu() function directly

In other words, what actually happens when you use a decorator is that the default argument passed in is the function being decorated.

import time
def count_time(func):
    def wrapper():
        t1 = ()
        func()
        print("Execution time is:", () - t1) 
    return wrapper  
@count_time
def baiyu():
    print("I'm White Jade, the Attack Lion.")
    (2) 
if __name__ == '__main__':
    # baiyu = count_time(baiyu) # Since the decorator count_time(baiyu) returns the function object wrapper, this statement is equivalent to baiyu = wrapper.
    # baiyu() # Executing baiyu() is equivalent to executing wrapper()
     baiyu()  # After using syntactic sugar,You can then call the function directly

V. Decorator Passing Parameters

When our decorated function is with parameters, how do we write the decorator at this point?

Above we have a blog function defined with the parameter

def blog(name):
    print('Enter the blog function')
    name()
    print('My blog is /zhh763984017')

At this point, our decorator function should be optimized to accept any parameter.

def count_time(func):
    def wrapper(*args,**kwargs):
        t1 = ()
        func(*args,**kwargs)
        print("Execution time is:", () - t1) 
    return wrapper

Here, the arguments to our wrapper function are *args and **kwargs, indicating that it can take arbitrary arguments

At this point we can call our decorator.

import time
def count_time(func):
    def wrapper(*args, **kwargs):
        t1 = ()
        func(*args, **kwargs)
        print("Execution time is:", () - t1)
     return wrapper
@count_time
def blog(name):
    print('Enter the blog function')
    name()
    print('My blog is /zhh763984017')
  if __name__ == '__main__':
    # baiyu = count_time(baiyu) # Since the decorator count_time(baiyu) returns the function object wrapper, this statement is equivalent to baiyu = wrapper.
    # baiyu() # Executing baiyu() is equivalent to executing wrapper()
 
    # baiyu() # After using syntactic sugar, you can call the function directly
    blog(baiyu)

VI. Decorators with parameters

We know that the decorator function is also a function, since it is a function, it can be passed parameters, how do we write a decorator with parameters?

Our decorator just implements a count, so I want to pass in some memo msg information when I use the decorator, what should I do? Let's take a look at the following code

import time
def count_time_args(msg=None):
    def count_time(func):
        def wrapper(*args, **kwargs):
            t1 = ()
            func(*args, **kwargs)
            print(f"[{msg}]The execution time is:", () - t1)
         return wrapper 
    return count_time
@count_time_args(msg="baiyu")
def fun_one():
    (1) 
@count_time_args(msg="zhh")
def fun_two():
    (1)  
@count_time_args(msg="mylove")
def fun_three():
    (1)  
if __name__ == '__main__':
    fun_one()
    fun_two()
    fun_three()

Let's based on the original count_time function and then wrapped in a layer of count_time_args used to receive parameters, the parameters received back can be called directly in the internal function. The effect of the above code is as follows:

VII. Class Decorators

Above we learned how to write decorator functions together, in python, in fact, you can also be the same kind to realize the function of the decorator, called class decorator. Class decorators are implemented by calling the __call__ function inside the class. Writing class decorators is simpler than writing our decorator functions.

Workflow when we use the class as a decorator:

  • Initialize the class via the __init__() method
  • Calling real decoration methods via the __call__() method
import time
class BaiyuDecorator:
    def __init__(self, func):
         = func
        print("Execute the __init__ method of the class.") 
    def __call__(self, *args, **kwargs):
        print('Enter the __call__ function')
        t1 = ()
        (*args, **kwargs)
        print("Execution time is:", () - t1)
@BaiyuDecorator
def baiyu():
    print("I'm White Jade, the Attack Lion.")
    (2)  
def python_blog_list():
    (5)
    print('''[Python] Crawler battle, zero-basic first try crawler download images (with source code and analysis process)
    /zhh763984017/article/details/119063252 ''')
    print('''[Python] In addition to multithreading and multiprocessing, you also need to know about concurrency
    /zhh763984017/article/details/118958668 ''')
    print('''[Python] Crawler speed up tips, multi-threading and multi-process (with source code examples)
    /zhh763984017/article/details/118773313 ''')
    print('''[Python] Crawler parsing tool Xpath, from shallow to deep quickly mastered (with source code examples)
    /zhh763984017/article/details/118634945 ''') 
@BaiyuDecorator
def blog(name):
    print('Enter the blog function')
    name()
    print('My blog is /zhh763984017') 
if __name__ == '__main__':
    baiyu()
    print('--------------')
    blog(python_blog_list)

VIII. Class decorators with parameters

The __init__() function can't pass in func (which represents the function to be decorated) when the decorator has arguments, and func is passed in when the __call__ function is called.

class BaiyuDecorator:
    def __init__(self, arg1, arg2):  # The arguments inside the init() method are all arguments to the decorator
        print('executable classDecorator(used form a nominal expression)__init__()methodologies')
        self.arg1 = arg1
        self.arg2 = arg2
     def __call__(self, func):  # Because the decorator takes parameters, the location to receive incoming function variables is here
        print('executable classDecorator(used form a nominal expression)__call__()methodologies') 
        def baiyu_warp(*args):  # The function name of the decorator here can be anything you want, as long as it's the same as the function name of the return.
            print('fulfillmentwrap()')
            print('Decorator parameters:', self.arg1, self.arg2)
            print('Execution' + func.__name__ + '()')
            func(*args)
            print(func.__name__ + '()fulfillment完毕') 
        return baiyu_warp 
@BaiyuDecorator('Hello', 'Baiyu')
def example(a1, a2, a3):
    print('transmitted inwardsexample()(used form a nominal expression)参数:', a1, a2, a3) 
if __name__ == '__main__':
    print('ready to invokeexample()')
    example('Baiyu', 'Happy', 'Coder')
    print('Test code execution complete')

We suggest that you compare the difference between the code here and the decorator code without parameters to deepen your understanding.

IX. Order of decorators

A function can be decorated by multiple decorators, so what is the order of execution of the decorators? Let's execute the following code to find out

def BaiyuDecorator_1(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I am decorator 1') 
    return wrapper 
def BaiyuDecorator_2(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I am decorator 2')
     return wrapper 
def BaiyuDecorator_3(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I am decorator 3') 
    return wrapper 
@BaiyuDecorator_1
@BaiyuDecorator_2
@BaiyuDecorator_3
def baiyu():
    print("I'm White Jade, the Attack Lion.") 
if __name__ == '__main__':
    baiyu()

As you can see from the output, a function modified by a decorator executes the function of the original function first, and then executes the contents of the decorator sequentially from the inside to the outside.

The code for our function with three decorators is as follows:

@BaiyuDecorator_1
@BaiyuDecorator_2
@BaiyuDecorator_3
def baiyu():
    print("I'm White Jade, the Attack Lion.")

The above code can be viewed as the following code to understand why it is executed from the inside out

baiyu = BaiyuDecorator_1 (BaiyuDecorator_2 (BaiyuDecorator_3(baiyu)))

Above is the python decorator principle source code example analysis of the details, more information about python decorator principle please pay attention to my other related articles!