This article is participating in Python Theme Month. See the link for details

What is a decorator

What is a closure? We can think of a decorator as a closure function that also returns a function.

You can also think of a decorator as passing a function as an argument to a method, processing it, and returning it.

Usage scenarios for decorators

1. Additional functions (unified log format, timer)

2. Data cleaning and adding (injecting data similar to dynamic token and modifying internal data cleaning)

3. Function registration, etc

Demo of decorator

1. How long it takes to implement a function before and after execution

Decorator use format: @decorator

Define the decorator
import time
def execTime(func) :
    def inner(*args,**kwargs) :    # receive parameters
        time1 = time.time()
        res = func(*args,**kwargs)
        time2 = time.time()
        print(func.__name__,'Execution cost',time2-time1,'s')
        return res    Return the value of func
    return inner  Return inner method object

Use the execTime decorator on the execAdd method
@execTime
def execAdd(a,b) :
    time.sleep(1)
    return a+b

# call execAdd
res = execAdd(1.2)
print(res)
Copy the code

Running results:

Python3 test.py execAdd execution cost 1.0000572204589844 s 3Copy the code

2. Realize unified log printing

import time,logging
def execTime(func) :
    def inner(*args,**kwargs) :
        time1 = time.time()
        Add a log to the start of the method
        logging.warning('now func {} start... '.format(func.__name__))
        res = func(*args,**kwargs)
        # add method end log
        logging.warning('now func {} finished... '.format(func.__name__))
        time2 = time.time()
        print(func.__name__,'Execution cost',time2-time1,'s'.'\n')
        return res
    return inner

@execTime
def execAdd(a,b) :
    time.sleep(1)
    return a+b

@execTime
def sayHi(name) :
    print('hello,{}.'.format(name))

sayHi('phyger')
res = execAdd(1.2)
print(res)
Copy the code

The output

python3 test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost0.00700068473815918s WARNING:root:now func execAdd start... WARNING:root:now func execAdd finished... ExecAdd Execution cost1.0020573139190674 s

3
Copy the code

You can see that both methods are added to the start and end alarm log before and after execution, and the execution time of the method is counted. This way we can easily encapsulate the method.

Pay attention to

When we execute the following code:

@execTime
def sayHi(name) :
    print('hello,{}.'.format(name))
Copy the code

The output

python3 test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost 0.018001317977905273 s innerCopy the code

The name of the sayHi method is the inner function of the decorator, which is not what we expected. The name and comment of our method have been overridden by the decorator function. How can we fix this problem?

Python provided a function for me to solve this problem, which is functools.wraps.

Let’s rewrite the decorator to see what it looks like:

from functools import wraps
import time,logging
def execTime(func) :

    @wraps(func)
    def inner(*args,**kwargs) :
        time1 = time.time()
        logging.warning('now func {} start... '.format(func.__name__))
        res = func(*args,**kwargs)
        logging.warning('now func {} finished... '.format(func.__name__))
        time2 = time.time()
        print(func.__name__,'Execution cost',time2-time1,'s'.'\n')
        return res
    return inner
    
# call
sayHi('phyger')
print(sayHi.__name__)
Copy the code

The output

test.py WARNING:root:now func sayHi start... hello,phyger. WARNING:root:now func sayHi finished... SayHi execution cost 0.003000020980834961 s sayHiCopy the code

You can see that the __name__ value of the sayHi method using the decorator is already its own value.

If you want to add functionality to your current decorator, you might want to use a class decorator, because class inheritance is a great way to address this need. More on class decorators later.