python 装饰器

什么是装饰器

装饰器本质上是一个函数,是利用闭包不改变原函数调用方式的基础上给原函数增加额外的功能,如登陆、验证、日志等。装饰器的返回值是一个函数对象

 下面来看一个例子,如果要计算函数的执行时间,怎么做呢

version 1.0

 1 import time
 2 
 3 
 4 def func1():
 5     time.sleep(0.2)
 6     print("这里有一段很复杂的代码")
 7 
 8 
 9 start_time = time.time()
10 func1()
11 end_time = time.time()
12 print(end_time-start_time)

 这样虽然也可以计算func1的返回时间,但是如果要计算500个函数呢,那相应的代码就得写500次,是不是很麻烦啊,有没有更简单的办法呢,有的

version 2.0

 1 def timer(f):
 2     def inner():
 3         start_time = time.time()
 4         f()
 5         end_time = time.time()
 6         print(end_time-start_time)
 7     return inner
 8 
 9 
10 func1 = timer(func1)
11 func1()

如果要计算别的函数的执行时间,只要继续调用timer函数,把参数换成相应的函数名就可以了。2.0版本看起来已经可以了,但是一细想就会发现问题,2.0的版本只适用没有参数的函数,如果函数有参数就不行了,所以我们还需要进一步优化,那么我们从哪里进行优化呢,当我们传参给func1时,实际上参数是传给了inner函数,我们看到的func1实际上是披着func1皮的inner函数,给func1传参实际上是传到inner函数里去了,因此我们需要修改inner函数,使其具备接收参数的功能,这里有一点需要注意,因为我们不知道func1需要接收几个参数,也为了使inner函数具有通用性,因此我们需要设置动态传参。优化后的代码如下

version 3.0

 1 def timer(f):
 2     def inner(*args, **kwargs):
 3         start_time = time.time()
 4         f(*args, **kwargs)
 5         end_time = time.time()
 6         print(end_time-start_time)
 7     return inner
 8 
 9 
10 func1 = timer(func1)
11 func1(2, 3)

3.0的版本虽然已经做了优化,但还是不能满足需求,因为如果需要测试的函数有很多,func1=timer(func1)这句话就要写很多次。python给我们提供了一个语法糖的功能,能够较好地简化这样的工作,使用方法是在要测试的函数前面加上@timer,相应的代码如下:

 1 def timer(f):
 2     def inner(*args, **kwargs):
 3         start_time = time.time()
 4         f(*args, **kwargs)
 5         end_time = time.time()
 6         print(end_time-start_time)
 7     return inner
 8 
 9 
10 @timer
11 def func2(a, b, c):
12     time.sleep(0.2)
13     print("这里有一段很复杂的代码")
14     print(a+b+c)
15 
16 
17 @timer
18 def func3(a, b):
19     time.sleep(0.2)
20     print("这里有一段很复杂的代码")
21     print(a*b)
22 
23 
24 func2(2, 3, 4)
25 func3(3, 4)

来看看执行结果

这里有一段很复杂的代码
9
0.20097684860229492
这里有一段很复杂的代码
12
0.20041775703430176

上述的装饰器已经接近完美了,唯一的不足就是查看函数信息的方法在此处会失效,

查看函数信息

1 def index():
2     """这是一个主页信息"""
3     print("from index")
4 
5 
6 print(index.__doc__)   # 查看函数注释的方法
7 print(index.__name__)  # 查看函数名的方法

解决办法

 1 from functools import wraps
 2 
 3 
 4 def deco(func):
 5     @wraps(func)   # 加在最内层函数正上方
 6     def wrapper(*args, **kwargs):
 7         return func(*args, **kwargs)
 8     return wrapper
 9 
10 
11 @deco
12 def index():
13     """hhhhh"""
14     print("from index")
15 
16 
17 print(index.__doc__)
18 print(index.__name__)

看到这里你对装饰器应该有了一个大概的了解,装饰器就是在不改变函数代码的情况下给函数扩展一些功能,这就涉及到了程序的开放和封闭原则。

开放封闭原则

开放封闭原则分为两个方面

1. 对扩展是开放的

任何一个程序,都不可能在设计之初就想好了所有功能并且未来不做任何更新和修改,所以我们必须允许允许代码扩展,添加新功能

2. 对修改是封闭的

为什么要对修改封闭呢?因为你要修改的函数很有可能已经交付给其他人使用了,如果这个时候对其进行修改,很有可能会影响正在使用该函数的用户。

我们上面写的装饰器完美地遵循了开放封闭原则:既没有改变原函数,又给它增加了新功能。

装饰器的基本结构

下面来总结一下,装饰器的基本结构

结构一:

1 def wrapper(func):
2     def inner(*args, **kwargs):
3         """执行函数之前要做的"""
4         re = func(*args, **kwargs)
5         """执行函数之后要做的"""
6         return re
7     return inner

结构二:

1 def deco(func):
2     @wraps(func)
3     def wrapper(*args, **kwargs):
4         return func(*args, **kwargs)
5     return wrapper

带参数的装饰器

装饰器也可以加上参数,通过参数来控制是否对函数进行装饰

 1 def outer(flag):
 2     def timer(func):
 3         def inner(*args, **kwargs):
 4             if flag:
 5                 print("执行函数之前要做的")
 6             re = func(*args, **kwargs)
 7             if flag:
 8                 print("执行函数之后要做的")
 9             return re
10         return inner
11     return timer
12 
13 
14 @outer(True)
15 def func():
16     print(111)

执行结果

执行函数之前要做的
111
执行函数之后要做的

把参数改为False

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

执行结果

111

多个装饰器装饰同一个函数

如果需要扩展的功能很多,那么就需要多个装饰器,

 1 def wrapper1(func):
 2     def inner():
 3         print("wrapper1, before func")
 4         func()
 5         print("wrapper1, after func")
 6     return inner
 7 
 8 
 9 def wrapper2(func):
10     def inner():
11         print("wrapper2, before func")
12         func()
13         print("wrapper2, after func")
14     return inner
15 
16 
17 @wrapper2
18 @wrapper1
19 def f():
20     print("in f")
21 
22 
23 f()

执行结果

wrapper2, before func
wrapper1, before func
in f
wrapper1, after func
wrapper2, after func

好了,装饰器到这里就结束了,来总结一下吧

装饰器总结

定义:装饰器是一个函数,这个函数可以在不改变被装饰函数函数代码的前提下给被装饰函数增加额外的功能

特点:(1)装饰器本身是一个函数

   (2)装饰器的返回值也是一个函数

   (3)装饰器完美遵循开放封闭原则

   (4)装饰器可以加上参数,控制是否对函数进行装饰

   (5)可以多个装饰器一起装饰一个函数

应用场景:插入日志,性能测试,事务处理,缓存等

 

原文地址:https://www.cnblogs.com/zzliu/p/10220561.html