[Python] Understand Python decorators (with source code examples)

Posted by smithveg on Thu, 02 Sep 2021 20:39:31 +0200

Catalog

Preface

1. What is an ornament

2. Why use ornaments

3. Simple ornaments

4. Grammatical sugar for ornaments@

5. Decorator Hands-on

6. Decorators with parameters

7. Class Decorators

8. Class ornaments with parameters

9. Order of ornaments

summary

Write after

Preface

Recently, when someone asked me what a decoration is, I told him that it was actually a decoration that resembled a girl's hairpin.A girl you like can have many haircards. When she wears different haircards, she has different haircards on top of her head.But the girl you like is the one you like.If you still don't understand it, the ornament is our mobile phone shell. Although you put on the mobile phone shell, it doesn't affect the function of your mobile phone, but your mobile phone should still be able to play for you. Call you, play games, and play games. This collection of Siege Lion Baiyu is for Siege Lion Baiyu's blog.And your phone becomes a phone with a mobile phone shell.

The ornament is one of python's road tigers. If you do or don't do it, it's all there.If you want to learn advanced Python usage, the ornament is a tiger that you Wusong must knock down.

The environment for this article is as follows:

win10,python3.7

1. What is an ornament

Decorator is a small addition to existing modules, which can extend the functions of the original function without modifying the content of the original function or the call of the original function.

The use of decorators conforms to the open and closed principle of object-oriented programming.

The principle of openness and closure is mainly reflected in two aspects:

  1. Open to extensions means that existing code can be extended to accommodate new requirements or changes.
  2. Closed to modifications means that once a class is designed, it can work independently of any modifications to the class.

2. Why use ornaments

Before you use the decorator, we need to know that everything in python is an object, that is, everything can be transmitted.

Functions can also be passed as parameters to a function.

This simple example provides a more intuitive way to see how function names are passed directly as parameters

def baiyu():
    print("I am Siege Lion Baiyu")


def blog(name):
    print('Get into blog function')
    name()
    print('My blog is https://blog.csdn.net/zhh763984017')


if __name__ == '__main__':
    func = baiyu  # Here we assign the function name baiyu to the variable func
    func()  # Execute func function
    print('------------')
    blog(baiyu)  # Pass the function baiyu as an argument to the blog function

The results are as follows:

 Next, I want to know how long the baiyu and blog functions take to execute, so I'll modify the code as follows:

import time


def baiyu():
    t1 = time.time()
    print("I am Siege Lion Baiyu")
    time.sleep(2)
    print("The execution time is:", time.time() - t1)


def blog(name):
    t1 = time.time()
    print('Get into blog function')
    name()
    print('My blog is https://blog.csdn.net/zhh763984017')
    print("The execution time is:", time.time() - t1)


if __name__ == '__main__':
    func = baiyu  # Here we assign the function name baiyu to the variable func
    func()  # Execute func function
    print('------------')
    blog(baiyu)  # Pass the function baiyu as an argument to the blog function

 

 The above overrides have already fulfilled the functions I need, but when I have another new function [python_blog_list) as follows:

def python_blog_list():
    print('''[Python]Crawler battle, zero base initial crawler download pictures (with source code and analysis process)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''[Python]In addition to multithreading and multiprocessing, you also have to work together
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''[Python]Crawler speed-up tips, multithreading and multiprocessing (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''[Python]Crawler resolver Xpath,Quick mastery from shallow to deep (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')

You also need to calculate the execution time of the function, which, according to the previous logic, is rewritten as follows:

def python_blog_list():
    t1 = time.time()
    print('''[Python]Crawler battle, zero base initial crawler download pictures (with source code and analysis process)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''[Python]In addition to multithreading and multiprocessing, you also have to work together
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''[Python]Crawler speed-up tips, multithreading and multiprocessing (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''[Python]Crawler resolver Xpath,Quick mastery from shallow to deep (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')
    print("The execution time is:", time.time() - t1)

If you want to write like this, don't you repeat the wheel?Although human nature is a pigeon king and a repeater, being a good C V attack lion (ctrl+c and ctrl+v) is sure to find ways to be lazy.

 

Decorators, that is, they allow us to expand some functions that the original function does not have.

3. Simple ornaments

Based on the above function execution time requirements, we implemented a simple handwritten decorator.

import time


def baiyu():
    print("I am Siege Lion Baiyu")
    time.sleep(2)


def count_time(func):
    def wrapper():
        t1 = time.time()
        func()
        print("The execution time is:", time.time() - t1)

    return wrapper


if __name__ == '__main__':
    baiyu = count_time(baiyu)  # Because decorator count_The time function object wrapper returned by time (baiyu), which is equivalent to baiyu = wrapper
    baiyu()  # Executing baiyu() is equivalent to executing wrapper()

Count_hereTime is an ornament, the ornament function defines a wrapper function, the func function is passed in as a parameter, the function is to wrap the func and return the wrapper function.The wrapper function body is to implement the contents of the decorator.

Of course, the wrapper function name here is customizable, as long as the function name you define is the same as the function name you return ed

4. Grammatical sugar for ornaments@

If you've seen the code in other python projects, you'll inevitably see the @ symbol, which is the grammatical sugar for the decorator.So the simple decorator above can be achieved by grammatical sugar, which can be omitted

baiyu = count_time(baiyu)

This code calls the baiyu() function directly

In other words, what actually uses the decorator is that the default parameter passed in is the decorated function.

import time


def count_time(func):
    def wrapper():
        t1 = time.time()
        func()
        print("The execution time is:", time.time() - t1)

    return wrapper


@count_time
def baiyu():
    print("I am Siege Lion Baiyu")
    time.sleep(2)


if __name__ == '__main__':
    # baiyu = count_time(baiyu)  # Because decorator count_The time function object wrapper returned by time (baiyu), which is equivalent to baiyu = wrapper
    # baiyu()  # Executing baiyu() is equivalent to executing wrapper()

    baiyu()  # With the grammar sugar, you can call the function directly

5. Decorator Hands-on

When our decorated function is parameterized, how do we write the decorator?

Above we have defined a blog function that is parameterized

def blog(name):
    print('Get into blog function')
    name()
    print('My blog is https://blog.csdn.net/zhh763984017')

At this point, our decorator function should be optimized and modified to accept any parameter of the decorator

def count_time(func):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        func(*args,**kwargs)
        print("The execution time is:", time.time() - t1)

    return wrapper

Here, the parameters of our wrapper function are *args and **kwargs, indicating that any parameter can be accepted

Then we can call our decorator.

import time


def count_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args, **kwargs)
        print("The execution time is:", time.time() - t1)

    return wrapper


@count_time
def blog(name):
    print('Get into blog function')
    name()
    print('My blog is https://blog.csdn.net/zhh763984017')


if __name__ == '__main__':
    # baiyu = count_time(baiyu)  # Because decorator count_The time function object wrapper returned by time (baiyu), which is equivalent to baiyu = wrapper
    # baiyu()  # Executing baiyu() is equivalent to executing wrapper()

    # baiyu()  # With the grammar sugar, you can call the function directly
    blog(baiyu)

6. Decorators with parameters

Previously, we know that the decoration function is also a function. Since it is a function, then parameters can be passed. How can we write a decoration with parameters?

Our ornament just has a count, so what should I do if I want to pass in some notes msg information when using it?Let's take a look at the code below

import time


def count_time_args(msg=None):
    def count_time(func):
        def wrapper(*args, **kwargs):
            t1 = time.time()
            func(*args, **kwargs)
            print(f"[{msg}]The execution time is:", time.time() - t1)

        return wrapper

    return count_time


@count_time_args(msg="baiyu")
def fun_one():
    time.sleep(1)


@count_time_args(msg="zhh")
def fun_two():
    time.sleep(1)


@count_time_args(msg="mylove")
def fun_three():
    time.sleep(1)


if __name__ == '__main__':
    fun_one()
    fun_two()
    fun_three()

Let's base on the original count_Outside the time function is a layer of count_to receive parametersTime_Args, the parameters received can be called directly in the internal function.The above code executes as follows:

 

7. Class Decorators

Let's learn how to write the decorator function together. In python, in fact, the same kind of decorator can also achieve the function of decorator, called decorator like.The implementation of the class decorator calls u inside the classCall_uFunction.Class decorators are easier to write than our decorator functions.

When we use a class as an ornament, the workflow is:

  • Pass uInit_u()Method Initialization Class
  • Pass uCall_u()Method call s the real decorative method
import time


class BaiyuDecorator:
    def __init__(self, func):
        self.func = func
        print("Executing Class__init__Method")

    def __call__(self, *args, **kwargs):
        print('Get into__call__function')
        t1 = time.time()
        self.func(*args, **kwargs)
        print("The execution time is:", time.time() - t1)


@BaiyuDecorator
def baiyu():
    print("I am Siege Lion Baiyu")
    time.sleep(2)


def python_blog_list():
    time.sleep(5)
    print('''[Python]Crawler battle, zero base initial crawler download pictures (with source code and analysis process)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''[Python]In addition to multithreading and multiprocessing, you also have to work together
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''[Python]Crawler speed-up tips, multithreading and multiprocessing (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''[Python]Crawler resolver Xpath,Quick mastery from shallow to deep (with source code examples)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')


@BaiyuDecorator
def blog(name):
    print('Get into blog function')
    name()
    print('My blog is https://blog.csdn.net/zhh763984017')


if __name__ == '__main__':
    baiyu()
    print('--------------')
    blog(python_blog_list)

8. Class ornaments with parameters

When the decorator has parameters, uInit_u()Functions cannot be passed into func (func stands for the function to be decorated), and func is in uCall_uPassed in when the function is call ed.

 

class BaiyuDecorator:
    def __init__(self, arg1, arg2):  # The parameters inside the init() method are ornament parameters
        print('Execution Class Decorator Of__init__()Method')
        self.arg1 = arg1
        self.arg2 = arg2

    def __call__(self, func):  # Because the decorator takes parameters, the location where the incoming function variable is received is here
        print('Execution Class Decorator Of__call__()Method')

        def baiyu_warp(*args):  # The decorator's function name can be freely named here as long as it is the same as the return ed function name
            print('implement wrap()')
            print('Decorator parameters:', self.arg1, self.arg2)
            print('implement' + func.__name__ + '()')
            func(*args)
            print(func.__name__ + '()completion of enforcement')

        return baiyu_warp


@BaiyuDecorator('Hello', 'Baiyu')
def example(a1, a2, a3):
    print('afferent example()Parameters:', a1, a2, a3)


if __name__ == '__main__':
    print('Ready to invoke example()')
    example('Baiyu', 'Happy', 'Coder')
    print('Test code execution completed')

I suggest you compare the code here with the decorator code without parameters to gain a better understanding.

9. Order of ornaments

A function can be decorated by multiple decorators, so what is the order in which the decorators are executed?Let's execute the code below to make it clear

def BaiyuDecorator_1(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I'm Decorator 1')

    return wrapper

def BaiyuDecorator_2(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I'm Decorator 2')

    return wrapper

def BaiyuDecorator_3(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('I'm Decorator 3')

    return wrapper

@BaiyuDecorator_1
@BaiyuDecorator_2
@BaiyuDecorator_3
def baiyu():
    print("I am Siege Lion Baiyu")


if __name__ == '__main__':
    baiyu()

From the output results, the function decorated by the decorator is executed first, then the decorator's content is executed from inside to outside.

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

 

@BaiyuDecorator_1
@BaiyuDecorator_2
@BaiyuDecorator_3
def baiyu():
    print("I am Siege Lion Baiyu")

The above code can be seen as follows to understand why it was executed from the inside out

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

summary

This article introduces python's decorators in depth, and shows how to write your own handwritten decorator functions and class decorators through code.

Write after

If you find it useful, take the trouble to support Siege Lion Baiyu one by one, and share this article with more partners.Your simple support, my unlimited creative power

Topics: Python