Talk about the @ symbol in Python

  • 2021-11-30 01:03:15
  • OfStack

The @ symbol in Python means decorator. In Python, the decorator is essentially a function, which allows other functions to add additional functions without any code changes. The return value of the decorator is also a function object (pointer to a function).

Essence: Is a function Parameter: Is the name of the function you want to decorate (not the function call) Return: Is the decorated function name (also not the function call) Function: Adds additional functionality to existing objects. Features: There is no need to make any code changes to the object.

Python decorator has many classic application scenarios, such as: log insertion, performance testing, transaction processing, permission verification and so on. Decorators are an excellent design to solve such problems.

The biggest function of the decorator is that for the program we have written, we can pull out a number of similar code components and multiple specific decorators, so that we can use specific decorators for different needs. At this time, because the source code removes a large number of generalized contents, the source code has clearer logic.

Define an doctorator that can print logs:


def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper


@log
def now():
    print('2021-3-25')


if __name__ == '__main__':
    now()

Implementation results:

The function object has a __name__ attribute, so you can get the name of the function.

Calling the now () function not only runs the now () function itself, but also prints 1 line of log before running the now () function.

Putting @ log at the definition of the now () function is equivalent to executing a statement:

now = log(now)

The parameter definitions of the wrapper () function are (*args, **kw), so the wrapper () function can receive calls of any parameter. Within the wrapper () function, the log is printed first, and then the original function is called.

If decorator itself needs to pass in parameters, you need to write a higher-order function that returns decorator:


def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator


@log('execute')
def now():
    print('2015-3-25')


if __name__ == '__main__':
    now()

Implementation results:

Compared with decorator with two layers of nesting, the effect of three layers of nesting is as follows:


now = log('execute')(now)

First, execute log ('execute'), return decorator function, then call the returned function, the parameter is now function, and the return value is wrapper function.

Functions are also objects and have properties like __name_, but if you look at functions decorated with decorator, their __name__ has changed from 'now' to 'wrapper':


print(now.__name__) # Output: wrapper

Because the name of the returned wrapper () function is' wrapper ', you need to assign attributes such as __name__ of the original function to the wrapper () function, otherwise, some code that depends on the function signature will execute in error.

functiontools. wraps built into Python does this, so a complete decorator is written as follows:


import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

Or


import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

Summarize


Related articles: