[Python advanced] Decorator

Posted by adredz on Fri, 31 Dec 2021 14:31:57 +0100

Decorator

Learning objectives

  • Be able to know the syntax format of defining decorators

1. Definition of decorator

It is a function that adds additional functions to existing functions. It is essentially a closure function.

Functional features of decorator:

  1. Do not modify the source code of existing functions
  2. Do not modify the calling method of existing functions
  3. Add additional functions to existing functions

2. Example code of decorator

# Add a login verification function
def check(fn):
    def inner():
        print("Please log in first....")
        fn()
    return inner
def comment():
    print("Comment")

# Decorating functions with decorators
comment = check(comment)
comment()

# Basic rudiments of decorators
# def decorator(fn): # fn: objective function
#     def inner():
#         '' before executing function '' '
#         fn() # Execute decorated functions
#         '' after function execution '' '
#     return inner

Code Description:

  • A closure function has only one parameter and must be a function type. The function defined in this way is a decorator.
  • Writing code should follow the open and closed principle, which stipulates that the implemented function code is not allowed to be modified, but can be extended.

Execution results:

Please log in first....
Comment

3. Grammar of ornament

If multiple functions need to add login verification function, you need to write func = check(func) code every time to decorate the existing functions, which is still troublesome.

Python provides a simpler way to write decorative functions, that is, syntax sugar. The writing format of syntax sugar is: @ decorator name. You can also decorate existing functions through syntax sugar

# Add a login verification function
def check(fn):
    print("Decorator function executed")
    def inner():
        print("Please log in first....")
        fn()
    return inner

# Use syntax to decorate functions
@check
def comment():
    print("Comment")

comment()

explain:

  • @Check is equivalent to comment = check(comment)
  • The execution time of the decorator is immediately when the module is loaded.

Execution results:

Please log in first....
Comment
# Purpose of learning decorator: to extend the existing functions. Decorator is essentially a closure function, that is, it is also a nested function

# Features of decorator:
# 1. Do not modify the source code of existing functions
# 2. Do not modify the calling method of existing functions
# 3. Add additional functions to future functions

# Define decorator
def decorator(func):  # If a closure function has only one parameter and is a function type, the closure function is called a decorator
    print("The decorator has been executed")
    def inner():
        # Decorate existing functions in internal functions
        print("Login verification added")
        func()
    return inner


# Grammar sugar writing method of decorator: @ name of decorator. Grammar sugar of decorator is easier to write when decorating functions
@decorator  # The comment = decorator(comment) decorator syntax encapsulates the code
def comment():
    print("Comment")

# Login verification added
# Comment

# # Call the decorator to decorate the existing function, comment=inner
# comment = decorator(comment)

# The calling method remains unchanged
comment()  # Before this code is executed, the decorator has been executed

# Execution timing of decorator: when the current module is loaded, the decorator will execute immediately to decorate the existing functions

4. Summary

  • Decorator is essentially a closure function, which can extend the existing functions.

  • Syntax format of decorator: it has only one parameter and is a function type

# Decorator
def decorator(fn): # fn: decorated objective function

     def inner():

         '''Before executing the function'''

         fn() # Execute the decorated objective function

         '''After executing the function'''

     return inner
  • Syntax and usage of decorator: @ decorator name, which can also complete the decoration operation of existing functions.
  • Execution timing of decorator: when the current module is loaded, the decorator will execute immediately to decorate the existing functions
  • Purpose of learning decorator: to extend the existing functions. Decorator is essentially a closure function, that is, it is also a nested function

Use of decorators

  • Be able to tell the function of the decorator

1. Use scenario of decorator

  1. Statistics of function execution time
  2. Output log information

2. The decorator implements the statistics of the execution time of existing functions

import time

# Decorator function
def get_time(func):
    def inner():
        begin = time.time()
        func()
        end = time.time()
        print("Function execution cost%f" % (end-begin))
    return inner
@get_time
def func1():
    for i in range(100000):
        print(i)

func1()

Execution results:

...
99995
99996
99997
99998
99999
 Function execution cost 0.329066
import time

# Define decorator
def decorator(func):
    def inner():
        # Internal functions decorate existing functions
        # Get the time difference from 1970-1-1:0:0:1
        begin = time.time()
        func()
        end = time.time()

        result = end - begin
        print("Function execution completion time:", result)

    return inner


@decorator  # work = decorator(work), work = inner
def work():
    for i in range(10000):
        print(i)

work()  # work = inner

2. Summary

Through the above example code, we can know the function of the decorator:

  • Extend the functions of existing functions without changing the source code and calling mode of existing functions.

Use of universal decorator

1. Decorate functions with parameters

def decorator(func):
    # When decorating an existing function with a decorator, the type of the inner function is consistent with the type of the existing function to be decorated
    def inner(a, b):
        # Decorate existing functions in internal functions
        print("Trying to perform an addition calculation")
        func(a, b)

    return inner


# Decorating functions with arguments in decorator syntax
@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(num1, num2):
    result = num1 + num2
    print("The result is:", result)

add_num(1, 2)

2. Decorate functions with return values

def decorator(func):
    # When decorating an existing function with a decorator, the type of the inner function is consistent with the type of the existing function to be decorated
    def inner(a, b):
        # Decorate existing functions in internal functions
        print("Trying to perform an addition calculation")
        num = func(a, b)
        return num

    return inner


# Decorating functions with arguments in decorator syntax
@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(num1, num2):
    result = num1 + num2
    return result

result = add_num(1, 2)
print("The result is:", result)

3. Decorate functions with indefinite length parameters

# The decorator can also become a universal decorator
def decorator(func):
    # When decorating an existing function with a decorator, the type of the inner function is consistent with the type of the existing function to be decorated
    def inner(*args, **kwargs):
        # Decorate existing functions in internal functions
        print("Trying to perform an addition calculation")

        # *args: pass each element in the tuple as a positional parameter
        # **kwargs: pass each key value pair in the dictionary as a keyword
        # The unpacking of tuples and dictionaries is limited to functions with indefinite length parameters
        num = func(*args, **kwargs)
        return num

    return inner

@decorator  # show= decorator(show) show = inner
def show():
    return "ha-ha"

result = show()
print(result)
@decorator  #  add_num= decorator(add_num), add_num=inner
def add_num(*args, **kwargs):
    result = 0

    # args: tuple type
    # kwargs: dictionary type

    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    return result

result = add_num(1, 2)
print("The result is:", result)


if __name__ == '__main__':
    my = {"a":1}
    print(**my)
  • When decorating an existing function with a decorator, the type of the inner function is consistent with the type of the existing function to be decorated

Use of multiple decorators

1. Use example code of multiple decorators

def make_div(func):
    print("make_div The decorator has been executed")

    def inner():
        # Decorate existing functions in internal functions
        result = "<div>" + func() + "</div>"
        return result

    return inner


# Define decorator
def make_p(func):
    print("make_p The decorator has been executed")

    def inner():
        # Decorate existing functions in internal functions
        result = "<p>" + func() + "</p>"
        return result

    return inner

# Decoration process of multiple decorators: a decoration process from inside to outside. The internal decorator is executed first, and the external decorator is executed later
# Principle analysis: content = make_div(make_p(content))
# Step by step disassembly: content = make_p(content), interior decorator installation completed, content=make_p.inner
#  content = make_div(make_p.inner)
@make_div
@make_p
def content():
    return "Life is short, I use it python!"

# <p>Life is short, I use Python</ p>
result = content()
print(result)
make_p The decorator has been executed
make_div The decorator has been executed
<div><p>Life is short, I use it python!</p></div>

Decorator with parameters

1. Introduction to decorator with parameters

Decorators with parameters can pass in specified parameters when decorating functions with decorators. Syntax format: @ decorators (parameters,...)

Incorrect writing:

def decorator(fn, flag):
    def inner(num1, num2):
        if flag == "+":
            print("--Trying to add--")
        elif flag == "-":
            print("--Trying to subtract--")
        result = fn(num1, num2)
        return result
    return inner
@decorator('+')
def add(a, b):
    result = a + b
    return result

result = add(1, 3)
print(result)

Execution results:

Traceback (most recent call last):
  File "/home/python/Desktop/test/hho.py", line 12, in <module>
    @decorator('+')
TypeError: decorator() missing 1 required positional argument: 'flag'

Code Description:

  • The decorator can only receive one parameter and is also a function type.

Correct writing:

Wrap a function outside the decorator, let the outermost function receive parameters, and return the decorator, because the @ symbol must be followed by the decorator instance.

def return_decorator(flag):
    # Decorator, which can only receive one parameter and is a function type
    def decorator(func):
        def inner(a, b):
            if flag == "+":
                print("Trying to perform an addition calculation")
            elif flag == "-":
                print("Efforts are being made to perform subtraction calculations")
            func(a, b)
        return inner
    # When the function is called, a decorator decorator can be returned
    return decorator


# Addition calculation
@return_decorator("+")  # decorator = return_decorator("+"), @decorator => add_num=decorator(add_num)
def add_num(a, b):
    result = a + b
    print(result)

add_num(1, 2)

# # Subtraction calculation
# @return_decorator("-")
# def sub_num(a, b):
#     result = a - b
#     print(result)
#
# add_num(1, 2)
# sub_num(1, 4)

# A decorator with parameters actually defines a function, allows the function to receive parameters, and returns a decorator inside the function
  • A decorator with parameters actually defines a function, allows the function to receive parameters, and returns a decorator inside the function
  • Using a decorator with parameters actually wraps a function outside the decorator. The function receives parameters and returns the decorator, because the @ symbol needs to be used with the decorator instance

Use of class decorators

1. Introduction to class I decorators

Another special use of decorators is class decorators, which decorate functions by defining a class.

# # Class decorator: decorate existing functions with classes
class MyDecorator(object):
    def __init__(self, func):
        self.__func = func

    # Realize__ call__ This method turns the object into a callable object, which can be used like a function
    def __call__(self, *args, **kwargs):
        # Encapsulate existing functions
        print("The class is over")
        self.__func()


@MyDecorator  # @MyDecorator => show = MyDecorator(show)
def show():
    print("It's almost school")

# implement show  # Execute the MyDecorator class to create an instance object - > show() = > object ()
show()

# class AAA(object):
#     pass
#
# a = AAA()
# a()

# Extension: all functions can be called because they are used internally__ call__
def mytest():
    print("ha-ha")

print(dir(mytest))

explain:

  • @Check is equivalent to comment = Check(comment), so you need to provide an init method and add an fn parameter.
  • If you want the instance object of a class to be called like a function, you need to use the call method in the class to turn the instance of the class into a callable object, that is, you can call it like a function.
  • Decorate the fn function in the call method to add additional functions.
  • Class decorator: decorate existing functions with classes
  • Realize__ call__ This method turns the object into a callable object. The callable object can be used like a function (it cannot be called by default)
  • If you want the instance object of a class to be called like a function, you need to use the call method in the class to turn the instance of the class into a callable object
  • The decorator function of class decorator is added in the call method

come on.

thank!

strive!

Topics: Python Web Development Back-end