Decorator function area

wedge

As a python developer who can write functions, we are going to work in the company from today. Write a function and hand it over to other developers.

def func1():
    print('in func1')

At the end of the quarter, the company's leaders are going to give everyone performance bonuses, so they propose to review the results developed by everyone during this period. What are the review standards? It is to count the execution time of each function.

What are you going to do at this time?

If you think about it, this is easy to do, change the function:

copy code
import time
def func1():
    start = time.time()
    print('in func1')
    print(time.time() - start)

func1()
copy code

I came to the company for half a year, wrote 2000+ functions, and changed them one by one. One week has passed, and after the leaders have reviewed them, they will be deleted one by one. . . Another week has passed. . . Isn't this annoying?

You don't think so, you can't make yourself work hard and tell all the developers, now you all add a statement to calculate the time to your original code?

copy code
import time
def func1():
    print('in func1')

start = time.time()
func1()
print(time.time() - start)
copy code

Or not, because it is too troublesome for development colleagues.

then what should we do? You have an idea and write a timer function. . .

copy code
import time
def timer(func):
    start = time.time()
    func()
    print(time.time() - start)

def func1():
    print('in func1')


def func2():
    print('in func2')

timer (func1)
timer (func2)
copy code

Doesn't this look much simpler? No matter how many functions we write, we can call this timing function to calculate the execution time of the function. . . Although the cost of modification has become very small now, it still changes the calling method of this function for colleagues. If a colleague believes you and uses your method for 2w times in his code, then After he modifies the code, the boat of your friendship is completely overturned.

What you have to do is to let your colleagues still call func1, but to achieve the effect of calling the timer method.

copy code
import time
def timer(func):
    start = time.time()
    func()
    print(time.time() - start)

def func1():
    print('in func1')

func1 =timer #If it can be like this, it will be perfect. . . Sorry for the error
func1()
copy code

It is a pity that the above code will report an error, because the timer method needs to pass a func parameter, we cannot pass the parameter when assigning, because as long as func1 = timer(func1) is executed, the timer method will be executed directly, the following The sentence func1 makes no sense at all. At this point, our thinking seems to have reached a deadlock. . .

 

The formation process of the decorator

copy code
import time

def func1():
    print('in func1')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

func1 = timer (func1)
func1()
copy code

After so busy for so long, it finally took shape! It's basically perfect now, the only thing that's annoying is that it's still doing an assignment call. . .

You think it's annoying, and python developers also think it's annoying, so we provide us with a syntactic sugar to solve this problem!

copy code
import time
def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer # ==> func1 = timer (func1)
def func1():
    print('in func1')


func1()
copy code

Here, we can briefly summarize:

  The essence of a decorator: a closure function

  The function of the decorator: to extend the function of the original function without modifying the original function and its calling method

There is one last problem to be solved. The decorators we discussed just now are all decorating functions without parameters. Now what if we want to decorate a function with parameters?

copy code
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def func1(a):
    print(a)

func1(1)
copy code

In fact, it is not difficult to decorate a function with parameters, but what if you have two functions that need to pass different parameters?

copy code
import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer # ==> func1 = timer (func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
copy code

Now the problem of parameters has been solved perfectly, but what if your function has a return value?

copy code
import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))
copy code

The decorator just now is perfect, but the way we normally view some information about the function will fail here.

def index():
    '''This is a homepage message'''
    print('from index')

print(index.__doc__) #How to view function comments
print(index.__name__) #How to view the function name

In order not to invalidate them, we have to add a little bit to the decorator to complete it:

copy code
from functools import wraps

def deco(func):
    @wraps(func) #Add just above the innermost function
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''Ha ha ha ha'''
    print('from index')

print(index.__doc__)
print(index.__name__)
copy code

 

open closed principle

  1. is open to extension

    Why open up to extensions?

    We say that it is impossible for any program to have all the functions in mind at the beginning of the design and not make any updates and modifications in the future. So we have to allow the code to expand, add new functionality.

  2. is closed to modification

    Why is it closed for modification?

    As we just mentioned, because a function we wrote is likely to be delivered to other people for use, if we modify it at this time, it is likely to affect other users who are already using the function.

Decorators follow this open-closed principle perfectly.

The main function of the decorator and the fixed structure of the decorator

The main function of the decorator:

Add functions before and after the function without changing the way the function is called.

Fixed format for decorators:

copy code
def timer(func):
    def inner(*args,**kwargs):
        '''What to do before executing the function'''
        re = func(*args,**kwargs)
        '''What to do after executing the function'''
        return re
    return inner
copy code
copy code
from functools import wraps

def deco(func):
    @wraps(func) #Add just above the innermost function
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper
copy code

 

Decorator with parameters

Suppose you have thousands of functions that use a decorator, and now you want to remove all of them, what do you do?

Cancel one by one? Busy day and night for 3 days. . .

After two days, your leader has figured it out and let you add it. . .

copy code
def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''What to do before executing the function''')
            re = func(*args,**kwargs)
            if flag:
                print('''What to do after executing the function''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
copy code

 

Multiple decorators decorate the same function

Sometimes, we also use multiple decorators to decorate the same function.

copy code
def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
copy code

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325021257&siteId=291194637