Detailed analysis of python decorator

Posted by codecontractor on Sat, 14 Mar 2020 08:13:08 +0100

Detailed analysis of python decorator

python decorators is a function used to expand the function of the original function. The purpose is to add new functions to the function without changing the original function name (or class name).

The special feature of this function is that its return value is also a function. This function is embedded with the "original" function.

Generally speaking, if we want to expand the original function code, the most direct way is to modify it in the intrusion code, for example:

import time
def f():
    print("hello")
    time.sleep(1)
    print("world")
  1. This is our original function. Then we try to record the total execution time of this function. The simplest way is to change the original code:
import time
def f():
    start_time = time.time()
    print("hello")
    time.sleep(1)
    print("world")
    end_time = time.time()

    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)
  1. But in practice, sometimes the core code can't be changed directly, so we can define another function without changing the original code. (but the function needs to be executed again to take effect)
import time

def deco(func):
    start_time = time.time()
    f()
    end_time = time.time()
    execution_time = (end_time - start_time)*1000
    print("time is %d ms" %execution_time)

def f():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':

    deco(f)
    print("f.__name__ is",f.__name__)
    print()
  1. Here we define a function, deco, whose parameter is a function, and then embed timing function into this function. But if you want to expand the 10 million functions, you need to execute the deca () function 10 million times, so this is not ideal! Next, we can try to use the decorator to realize, first look at the most primitive appearance of the decorator.
import time

def deco(f):
    def wrapper():
        start_time = time.time()
        f()
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" %execution_time )
    return wrapper

@deco
def f():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
    f()
  • The deco function here is the most primitive decorator. Its parameter is a function, and then the return value is also a function.

The function f () as a parameter is executed inside the return function wrapper(). Then add @ deco before the function f(),

The f() function is equivalent to being injected with timing function. Now just call f() and it has become a function with "more new functions",

(you do not need to repeat the original function).

Extension 1: decorator with fixed parameters

import time

def deco(f):
    def wrapper(a,b):
        start_time = time.time()
        f(a,b)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
    return wrapper

@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

if __name__ == '__main__':
    f(3,4)

Extension 2: decorator without fixed parameters

import time

def deco(f):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time_ = (end_time - start_time)*1000
        print("time is %d ms" %execution_time)
    return wrapper


@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def f2(a,b,c):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b+c))


if __name__ == '__main__':
    f2(3,4,5)
    f(3,4)

Extension 3: use multiple decorators to decorate a function √

import time

def deco01(f):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
        print("deco01 end here")
    return wrapper

def deco02(f):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        f(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))


if __name__ == '__main__':
    f(3,4)
  • Execution sequence
  • For the "@" syntactic sugar in Python, the order in which the decorators are called is the reverse of the order in which they are declared using the @ syntactic sugar.

In this case, "f(3, 4) = deco01(deco02(f(3, 4)))".

Reference link:
1.https://blog.csdn.net/xiangxianghehe/article/details/77170585
2.https://www.cnblogs.com/yuzhanhong/p/9180212.html

61 original articles published, 38 praised, 30000 visitors+
Private letter follow

Topics: Python