Home >Backend Development >Python Tutorial >A closer look at Python decorator functions

A closer look at Python decorator functions

WBOY
WBOYforward
2022-06-24 12:50:422031browse

This article brings you relevant knowledge about python, which mainly organizes issues related to decorator functions, including the formation process, essence and function, advancement and optimization of decorators Let’s take a look at the content below. I hope it will be helpful to everyone.

A closer look at Python decorator functions

Recommended learning: python

If I write a function f

def f():
    print('hello')

, I want to know this paragraph The time required for function execution is easy to handle. I just need to change the code to the following

import time
def f():
    start = time.time()   #获取程序执行开始的时间
    print('hello')
    end = time.time()     #获取程序执行结束的时间
    print(end - start)    #得出函数f执行所要时间

f()

But then I wrote countless functions f2, f3...fn, and I want to know how long each function takes to execute. time, wouldn’t it be very annoying if we change it as above? Still not possible, because it would be too much trouble. then what should we do? So I had an idea and wrote a timer function. . .

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

def f():
    print('hello')


def f2():
    print('xorld')

timer(f)
timer(f2)

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

But if I just want to call this function in the original way f1(), f2(), fn(), The function can also add the function of calculating time while the original execution output result remains unchanged. Instead of calling timer(f), timer(f2) can calculate time. What should we do?

You will know how to solve this problem after reading the decorator function below



1. Decorator - Formation process

The following is the solution A simple version of the code for the above question:

import time

def f():
    print('hello')

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

f = timer(f)
f()

Still with this sentence, I just want to call this function in the original way f1(), f2(), fn(). The function outputs the result when it is originally executed. Under the premise of changing, the function of calculating time can be added, but I still have to write f = timer(f) in this string of code before the function f is executed. Does it feel annoying? Python developers also find it annoying, so Python developers provide us with a syntax sugar to solve this problem!



2. Decorator - First introduction to syntactic sugar

Use @timmer instead of f = timer(f), this is a sentence of syntactic sugar.

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

@timer   #==> 写着这句话就相当于执行了f = timer(f)
def f():
    print('hello')


f()


3. Decorator - Essence and Function

1. Essence

The essence of a decorator is a closure function

2. Function

      

Expand the original function function without modifying the original function and its calling method

4. Decoration Decorator - decorator with parameters and return value

  • 1. Decoration function with one parameter

The decorator we just wrote They all decorate functions without parameters. Now what should I do if I want to decorate a function with parameters?

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

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

f('hello')
  • 2. Decorate multiple functions with different parameters but no return value

In fact, it is not difficult to decorate functions with parameters. , but what if you have two functions and the parameters that need to be passed are different, for example, function func1 has two parameters func1(a,b), function func2 has only one parameter func2(a), and they all want to use this decorator Decoration to calculate function execution time? What to do about this? Then use the following 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'))

输出结果:
in func1
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 3. Decorate multiple functions with different parameters and return values

Now the parameter problem has been perfectly solved. But what if your function has a return value? You can't get the return value using the above code. So how do you solve this problem? Then look at the code below!

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

输出结果:
in func2 and get a:aaaaaa
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 4. Multiple decorators decorate the same function

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

ef wrapper1(func):   #func ----- f
    def inner1():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner1

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

@wrapper2       #f = wrapper2(f) ----->> wrapper2(inner1)  == inner2
@wrapper1       #f = wrapper1(f) = inner
def f():
    print('in f')
f()    #===>>inner2
#多个装饰器装饰同一个函数

输出结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func


5. Decorator - Decorator advancement and optimization

  • 1. Decorator with parameters

The above decorator is already very beautiful, but there is another problem. If I add @timer syntactic sugar to countless functions in the code, what if I don’t want to use it anymore? Everyone has to annotate it and work day and night for 3 days? Isn’t it particularly troublesome? In order to better recycle the decorators when not in use instead of annotating or deleting them one by one, we introduce the concept of decorators with parameters

'''
为了使装饰器不用时能够更好的回收而不是一个一个去注释或者删除
我们引入带参数的装饰器概念
'''

import time
'''FLAGE的目的是用它控制装饰器的开关,
那么当我们不用的时候就不要一个一个去注释只需将True改为False就行'''

FLAGE = True
def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer
@timmer_out(FLAGE)

#timmer_out(FLAGE)
# 也相当于执行  timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha))
def wahaha():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('wahahahahahaha')

wahaha()

@timmer_out(FLAGE)
def erguotou():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('erguotoutoutou')

erguotou()

输出结果:
wahahahahahaha
0.10152268409729004
erguotoutoutou
0.10795140266418457
  • 2、防止函数必要信息失效

'''
print(wahaha.__name__)      #查看字符串格式的函数名
print(wahaha.__doc__)       #查看一个函数的注释
'''
#下面用__name__查看holiday的函数名

from functools import wraps
def wrapper(func):
    @wraps(func)            #加在最内层函数正上方
    def inner(*args,**kwargs):
        print('在被装饰的函数执行之前做的事')
        ret = func(*args,**kwargs)
        print('在被装饰的函数执行之后做的事')
        return ret
    return inner

@wrapper        #holiday = wrapper(holiday)
def holiday(day):
    '''
    这是一个放假通知
    :param day:
    :return:
    '''
    print('全体放假%s天'%day)
    return '好开心'

print(holiday.__name__)
print(holiday.__doc__)
'''
结果是inner和None 但我们想要的是打印holiday的字符串格式的函数名和函数的注释这时该怎么办?
解决方法就是  from functools import wraps
使用语法是@wraps(被装饰的函数名)
'''

输出结果:
holiday

    这是一个放假通知
    :param day:
    :return:


六、装饰器 —— 装饰原则

  • 1、开放封闭原则

1.对原函数的功能扩展是开放的

        为什么要对功能扩展开放呢?

    对于任何一个程序来说,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许后来扩展、添加新功能。

2.对修改是封闭的

        为什么要对修改封闭呢?

                就像我们刚刚提到的,因为我们写的一个函数,很有可能在其他地方已经被导入使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经正在使用该函数的代码。

装饰器就完美遵循了这个开放封闭原则。这就是学装饰器的初衷



小结:

  • 1、装饰器的固定格式(模板)

#格式一

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

#格式二

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

推荐学习:python

The above is the detailed content of A closer look at Python decorator functions. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete