An article to understand all the usage of decorator Python

01. Decorator syntactic sugar

If you have been in contact with Python for a while, you must be familiar with the @ symbol. Yes, the @ symbol is syntactic sugar for decorators.

It is placed where a function starts to be defined, and it is worn on the head of this function like a hat. Bind to this function. When we call this function, the first thing is not to execute this function, but to pass this function as a parameter to this hat on top of our head. This hat is called a decoration function or decorator.

You have to ask me what function the decorator can achieve? I can only say that the decorator is as powerful as your brain.

The method of using the decorator is very fixed:

First define a decorative function (hat) (can also be implemented with classes and partial functions)

  • Then define your business function, or class (person)
  • Finally put this hat on this person's head

There are many simple uses of decorators, here are two common ones.

  • Log printer
  • Time timer

02. Getting started usage: log printer ##

The first is the log printer.

Functions realized:

Before the function is executed, first print a log to inform the host that I want to execute the function.
After the function is executed, you can't pat the ass and leave, but we have polite code, and then print a line of logs to inform the host, I am finished.

# 这是装饰函数
def logger(func):
    def wrapper(*args, **kw):
        print('我准备开始计算:{} 函数了:'.format(func.__name__))

        # 真正执行的是这行。
        func(*args, **kw)

        print('啊哈,我计算完啦。给自己加个鸡腿!!')
    return wrapper

If my business function is to calculate the sum of two numbers. After writing, put a hat directly on it.

@logger
def add(x, y):
    print('{} + {} = {}'.format(x, y, x+y))

Then let's calculate.

add(200, 50)

Come and see what is output, is it magical?

I am ready to start calculation: add function:

200 + 50 = 250

Aha, I'm done. Add yourself a drumstick!

03. Getting started usage: time timer ##

Let's take a look at the time timer
implementation function: as the name suggests, it is to calculate the execution time of a function.

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 这是装饰函数
def timer(func):
    def wrapper(*args, **kw):
        t1=time.time()
        # 这是函数真正执行的地方
        func(*args, **kw)
        t2=time.time()

        # 计算下时长
        cost_time = t2-t1 
        print("花费时间:{}秒".format(cost_time))
    return wrapper

Suppose, our function is to sleep for 10 seconds. In this way, we can better see whether the calculation time is reliable.

import time

@timer
def want_sleep(sleep_time):
    time.sleep(sleep_time)

want_sleep(10)

Take a look and output. It's really 10 seconds. Really evil! ! !

花费时间:10.0073800086975098

04. Advanced usage: function decorator with parameters ##

Through the simple introduction above, you probably have already felt the magical charm of decoration.

However, the use of decorators goes far beyond this. We will make this point clear today.

In the above example, the decorator cannot accept parameters. Its usage can only be applied to some simple scenarios. Decorators that do not pass parameters can only execute fixed logic on the decorated function.

If you have experience, you must be in the project often, and see some decorators with parameters.

The decorator itself is a function, and since it cannot be carried as a function, the function of this function is very limited. Only fixed logic can be executed. This is undoubtedly very unreasonable. And if we want to use the two contents to be roughly the same, it is just different logic in some places. If we don't pass parameters, we will write two decorators. Xiao Ming felt that it was intolerable.

So how does the decorator implement parameter passing, it will be more complicated and requires two levels of nesting.

Similarly, let's take an example.

When we execute these two functions, we will say a hello word based on their nationality.

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def american():
    print("我来自中国。")

def chinese():
    print("I am from America.")

When putting the decorator on the two of them, you must tell the decorator, which country is this person, and then the decorator will make a judgment and say hello.

This is how it looks after wearing a hat.

@say_hello("china")
def american():
    print("我来自中国。")

@say_hello("america")
def chinese():
    print("I am from America.")

Everything is ready, only a hat. Let's define it. Here we need two levels of nesting.

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def say_hello(contry):
    def wrapper(func):
        def deco(*args, **kwargs):
            if contry == "china":
                print("你好!")
            elif contry == "america":
                print('hello.')
            else:
                return

            # 真正执行函数的地方
            func(*args, **kwargs)
        return deco
    return wrapper

Do it

american()
print("------------")
chinese()

Look at the output.

你好!
我来自中国。
------------
hello.
I am from America

emmmm, this is very NB. . .

05. Advanced usage: class decorator without parameters ##

The above are all decorators based on functions. When reading other people's code, you can often find that there are also decorators based on classes.

Based on the implementation of the class decorator, two built-in functions __ call__ and __ init__ must be implemented.
__ init__: Receive decorated function
__ call__: Implement decoration logic.

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("[INFO]: the function {func}() is running..."\
            .format(func=self.func.__name__))
        return self.func(*args, **kwargs)

@logger
def say(something):
    print("say {}!".format(something))

say("hello")

Run it and see the output

[INFO]: the function say() is running...
say hello!

06. Advanced usage: class decorator with parameters ##

The above example with no parameters, you find that there is no, you can only print INFO level logs, under normal circumstances, we also need to print DEBUG WARNING and other level logs. This requires passing parameters to the class decorator and assigning a level to this function.

Class decorators with and without parameters are very different.

__ init__: No longer receives the decorated function, but receives the incoming parameters.
__ call__: Receive the decorated function and implement the decoration logic.

class logger(object):
    def __init__(self, level='INFO'):
        self.level = level

    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print("[{level}]: the function {func}() is running..."\
                .format(level=self.level, func=func.__name__))
            func(*args, **kwargs)
        return wrapper  #返回函数

@logger(level='WARNING')
def say(something):
    print("say {}!".format(something))

say("hello")

We specify the WARNING level and run it to see the output.

[WARNING]: the function say() is running...
say hello!

07. Using partial functions and classes to implement decorators ##

Most decorators are implemented based on functions and closures, but this is not the only way to make decorators.

In fact, Python has only one requirement for whether an object can be used in the form of a decorator (@decorator): the decorator must be a "callable" object.

For this callable object, we are most familiar with functions.

In addition to functions, classes can also be callable objects. As long as the __call__ function is implemented (the above boxes have been touched), there are also partial functions that are rarely used by people.

Next, let's talk about how to use classes and partial functions to achieve a distinctive decorator.

As shown below, DelayFunc is a class that implements __call__, delay returns a partial function, where delay can be used as a decorator. (The following code is taken from the Python craftsman: tips for using decorators)

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import time
import functools

class DelayFunc:
    def __init__(self,  duration, func):
        self.duration = duration
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'Wait for {self.duration} seconds...')
        time.sleep(self.duration)
        return self.func(*args, **kwargs)

    def eager_call(self, *args, **kwargs):
        print('Call without delay')
        return self.func(*args, **kwargs)

def delay(duration):
    """
    装饰器:推迟某个函数的执行。
    同时提供 .eager_call 方法立即执行
    """
    # 此处为了避免定义额外函数,
    # 直接使用 functools.partial 帮助构造 DelayFunc 实例
    return functools.partial(DelayFunc, duration)

Our business function is very simple, just add

@delay(duration=2)
def add(a, b):
    return a+b

Take a look at the implementation process

>>> add    # 可见 add 变成了 Delay 的实例
<__main__.DelayFunc object at 0x107bd0be0>
>>> 
>>> add(3,5)  # 直接调用实例,进入 __call__
Wait for 2 seconds...
8
>>> 
>>> add.func # 实现实例方法
<function add at 0x107bef1e0>

08. How to write decorators that can decorate? ##

When writing singleton patterns in Python, there are three common ways of writing. One of them is implemented with a decorator.

The following is a single case of the decorator version I wrote myself.

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333 
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
instances = {}

def singleton(cls):
    def get_instance(*args, **kw):
        cls_name = cls.__name__
        print('===== 1 ====')
        if not cls_name in instances:
            print('===== 2 ====')
            instance = cls(*args, **kw)
            instances[cls_name] = instance
        return instances[cls_name]
    return get_instance

@singleton
class User:
    _instance = None

    def __init__(self, name):
        print('===== 3 ====')
        self.name = name

You can see that we use the singleton decoration function to decorate the User class. It is not very common for decorators to be used on classes, but as long as you are familiar with the process of implementing decorators, it is not difficult to implement decorations on classes. In the above example, the decorator simply implements control over the generation of class instances.

The instantiation process, you can refer to my debugging process here to understand.

Published 705 original articles · Liked 862 · Visits 1.5 million +

Guess you like

Origin blog.csdn.net/sinat_38682860/article/details/105455366