Advanced Python - Decorator

Table of contents

Definition of decorator

The decorated function has parameters

The decorator function itself has parameters


Definition of decorator

装饰器 , English is called decorator .

When we develop Python code, we often encounter decorators.

Decorators in Python are usually used to decorate functions or methods of classes.

Usually the decorated function will add a little function on the basis of the original function.

For example, when we learned about the static method in the class earlier, we used the staticmethod decorator, and the decorated method added a layer of meaning, indicating that the method is a static method.

Usually a decorator is itself a function. So how does the decorator decorate another function?

Suppose you enter a company and the leader asks you to continue developing on the basis of the old code.

Suppose there are many functions in the code, all of which return time, such as the following.

import time
def getXXXTime():
    print()
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

If we need to prefix all such function return strings with: local time

At this time, we can completely , but  , like this 不去修改原来的函数 使用装饰器 

import time

# 定义一个装饰器函数
def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

def getXXXTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

# 装饰 getXXXTime
getXXXTime = sayLocal(getXXXTime)

print (getXXXTime())  

Before executing the following line of code,

getXXXTime = sayLocal(getXXXTime)

The name getXXXTime corresponds to a function object whose content is as follows

return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

But when the interpreter finishes executing this line of code, it redefines the getXXXTime variable as the return value of subsequent calls. sayLocal(getXXXTime) 

sayLocal(getXXXTime) What is the return value of the call?

We see that  sayLocal(getXXXTime) the function is defined like this:

def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

Therefore, its parameter func is passed in a function object, which is the original getXXXTime function.

Then a function wrapper is defined inside this function, and this internal function realizes adding  such a prefix in front of the result of the original getXXXTime function call. 当地时间:

The return value of the sayLocal function call is the inner function wrapper.

Then after getXXXTime has been decorated, it has actually become an internal function wrapper

So later I call the getXXXTime function, which is actually calling the wrapper.

So we say that the sayLocal function is a decorator, which decorates the behavior of the parameter function.

And the 14th line of code is to change the behavior corresponding to a function name to the behavior after decoration.

Corresponding to the 14th line of code in Python, there can be a more convenient way of writing, as follows

import time

def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

@sayLocal
def getXXXTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

print (getXXXTime())   

The way of writing the decorator function after the beginning is a kind of convenient way of writing. @sayLocal  语法糖 

When the Python interpreter finishes executing the following statement,

@sayLocal
def getXXXTime():
    ....

It is equivalent to executing such a statement

getXXXTime = sayLocal(getXXXTime)

This is more convenient and quick to write.


Some readers may ask, why bother? Isn't it easier for us to directly change the content of this function? like this

import time
def getXXXTime():
    curTime = time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())
    return f'当地时间: {curTime}'

Decorators are often used in libraries and frameworks for use by other developers.

The developers of these libraries anticipate that user-developed functions may require some enhanced functionality.

But the developers of these libraries can't change the user's code, so they can make these enhanced parts in the decorator function.

In this way, users only need to put @xxx in front of their functions to use these enhanced functions.

The decorated function has parameters

What if the function to be decorated has an unknown number of parameters?

Both functions like this should be decorated with

import time
def getXXXTimeFormat1(name):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} '


def getXXXTimeFormat2(name,place):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} , 采集地:{place}'

At this time, you can use variable parameters of the function, like this

import time

def sayLocal(func):
    def wrapper(*args,**kargs):
        curTime = func(*args,**kargs)
        return f'当地时间: {curTime}'
    return wrapper

@sayLocal
def getXXXTimeFormat1(name):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} '

@sayLocal
def getXXXTimeFormat2(name,place):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} , 采集地:{place}'

print (getXXXTimeFormat1('张三'))    
print (getXXXTimeFormat2('张三',place='北京'))    

Among them, all   parameter passing methods  that do not specify the parameter name can be accepted, such as *args '张三'

**kargs Can accept all   parameter passing methods that  specify parameter names , such asplace='北京'

The decorator function itself has parameters

Sometimes, the decorator function itself requires parameters. For example, if you want to develop a web server, before executing a certain API processing, you need to check whether the system has activated the corresponding function, then our decorator function needs to have parameters.

for example:

# 需要激活自动化测试功能
@activation_check('自动化测试')
def autotest_scene1(request):
    ...


# 需要激活性能测试功能
@activation_check('性能测试')
def loadtest_scene1(request):
    ...

Decorate a function with a parameterized decorator, which is essentially equivalent to

func_name = (decorator(params))(func_name)

Therefore, we should define

from activations import activatedCapList
def activation_check(capability):
    def wrapper1(func):
        def wrapper2(*args, **kwargs):
            # 如果该功能没有激活,直接返回
            if capability not in activatedCapList:
                return Response('功能未激活')
                
            # 已经激活,调用被装饰的函数进行处理
            return func(*args, **kwargs)
        return wrapper2
    return wrapper1

@activation_check('自动化测试')
def autotest_scene1(request):
    ...

@activation_check('性能测试')
def loadtest_scene1(request):
    ...

After the autotest_scene1 function is decorated, it is equivalent to calling:

autotest_scene1 = (activation_check(capability))(autotest_scene1)

The process is like this:

The return value of activation_check(capability) is the wrapper1 function,

The return value of wrapper1(autotest_scene1) is the wrapper2 function,

So after being decorated, actually calling autotest_scene1 becomes calling wrapper2,

wrapper2 is a closure function that contains two external objects capability and func

Guess you like

Origin blog.csdn.net/weixin_47649808/article/details/126325850