[Python advanced] decorator

Python decorator

What is a decorator

The decorator is to pass a function as an argument to another function (or an iterative object, callable), and then call back, @ is its syntax sugar

Four kinds of common decorative tools

1. Decorator without parameters

def logger(func):
    def wrapper(*args, **kwargs):
        print(f'prepare to run {func.__name__}')
        func(*args, **kwargs)
        print('finished...')

    return wrapper


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


add(1, 2)

2. Decorator with parameters

def logger(level='INFO'):
    def wrapper(func):
        def handle(*args, **kwargs):
            print(f'{level}: prepare to run {func.__name__}')
            func(*args, **kwargs)
            print('finished...')
        return handle
    return wrapper


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

add(1, 2)

3. Class decorator without parameters

Without parameters, the initialized parameter is the decorated function, which is a two-tier structure.

class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'{self.level}: prepare to run {func.__name__}')
        return self.func(*args, **kwargs)


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

add(1, 2)

4. Class decorator with parameters

With parameters, the initialized parameters are the parameters of decoration function, that is, three-tier structure.

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

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f'{self.level}: prepare to run {func.__name__}')
            return func(*args, **kwargs)

        return wrapper

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

add(1, 2)

Precautions for using decorators

1. Use wraps in functools

Keep the decorated function signature, otherwise the signature is the decorator object

from functools import wraps

def wrapper(func):
    @wraps(func) 
    def handle():
        pass
    return handle

@wrapper
def wrapped():
    pass

print(wrapped.__name__)
# wrapped

2. Decoration sequence

Execution sequence: wrapper1 > wrapper2 (wrapper1 decorates wrapper2, wrapper2 decorates func)

@wrapper1
@wrapper2
def func():
    pass

3. closure

def counter(func):
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print("{0} has been used: {1}x".format(func.__name__, wrapper.count))
        return res

    wrapper.count = 0
    return wrapper


@counter
def system_out(string):
    print(string)
    

system_out("A")
system_out("B")
# A
# system_out has been used: 1x
# B
# system_out has been used: 2x

Common scenes and code implementation of decorator

Log printing, permission control, processing function timeout (to be updated)

Reference resources

https://stackoverflow.com/que...

https://zhuanlan.zhihu.com/p/...

https://zhuanlan.zhihu.com/p/...

Tags: Python

Posted on Tue, 05 Nov 2019 06:19:57 -0800 by karatekid36