python decorator in detail

Links to the original text: https://www.cnblogs.com/yuzhanhong/p/9180212.html

python decorator 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, which is a function embedded with the "original" function.
Generally speaking, if we want to expand the original function code, the most direct way is to invade the code and modify it, for example:

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

This is our original function, and 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)

But in practice, sometimes the core code can not be changed directly, so without changing the original code, we can define a function. (But to take effect, you need to execute the function again)

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()

Here we define a function deco, whose parameter is a function, and then embeds the timing function into the function. But we want to expand these ten million functions.

That's to execute deco() 10 million times, so that's not ideal! Next, we can try to use decorators to achieve, first look at the original appearance of decorators.

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 the return value is also a function.

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

The f() function is equivalent to being injected with the timing function. Now, if only f() is called, it will become a function with more new functions.

There is no need to repeat the original function.

Extension 1: Decorators 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: Decorators 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: Decorate a function with multiple decorators

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)

Decorator Call Order

Decorator can be superimposed, so what is the sequence of code after using decorator?

For the "@" grammatical sugar in Python, the order in which the decorator is invoked is the reverse of the order in which the @ grammatical sugar declaration is used.

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

Python built-in decorator

There are three built-in decorators in Python that are class-related: static method, class method, and property.

staticmethod is a class static method, which differs from member methods in that it has no self parameter and can be invoked without instantiating the class.
The difference between classmethod and member method is that the first parameter received is not self (a pointer to a class instance), but cls (the specific type of the current class).
property is what attributes mean and represents information that can be accessed directly through class instances.
For static method and classmethod, we don't introduce them here. Let's look at property through an example.

Note that for the Python new-style class, if the member function decorated by the @var.setter decorator above is removed, the Foo.var attribute is read-only, and an exception is thrown when assigning with "foo.var='var 2'". However, for Python classic class, the declared attribute is not read-only, so even removing the "@var.setter" decorator will not cause an error.

summary

This paper introduces some uses of Python decorator. The code of the decorator is easy to understand. It's easy to understand if you use some examples to do the actual operation.

Keywords: Python Attribute

Added by cutups on Mon, 12 Aug 2019 12:49:20 +0300