4.9 装饰器
4.9.1 开放封闭原则
1.对扩展是开放的:允许代码扩展、添加新功能。
2.对修改是封闭的:不要改变源码。防止对函数内部进行修改,不能改变调用方式
4.9.2 装饰器初识
定义:在不改变原被装饰的函数的源代码以及调用方式下,为其添加一个额外的功能。
装饰器本身是一个函数
版本一:
import time
def index():
time.sleep(2)
print('装饰器')
def func(f):
def inner():
start_time = time.time() #格林威治时间
f()
end_time = time.time()
print(f"执行时间为{end_time - start_time}")
return inner
index()
index = func(index)
index() #实际执行的是inner()
上边两个 index() 输出的结果是不一样的,第二个index()的功能已经增加
这么做的优点在于:没有改变index的执行方式,如果整个py文件里调用了100次index(),只需要这一步函数,不重要其他的修改,就可以神不知鬼不觉地进行了功能上的升级。
版本二:带自由变量的装饰器
import time
def index():
time.sleep(2)
print('装饰器')
def func(f):
f = index # 自由变量 f 存在的内存地址与全局和临时空间并列,不会因为函数的结束而消失,对其的赋值必须是要修饰的函数的变量名,因为在内部函数时要执行,此时它接收到的时函数index指向的内存地址
def inner():
start_time = time.time()
f()
end_time = time.time()
print(f"执行时间为{end_time - start_time}")
return inner
index = func(index)
index() #实际执行的是inner()
版本三:语法糖装饰器(标准版)
一般情况下,应该把装饰器函数放在需要加装饰器的函数之上
把 @装饰器名 放在需要加装饰器的函数名上边
# 语法糖装饰器
import time
# func装饰器
def func(f):
def inner():
start_time = time.time()
f()
end_time = time.time()
print(f"执行时间为{end_time - start_time}")
return inner
@func # 等同于 index = func(index)
def index():
time.sleep(2)
print('装饰器')
def copare():
time.sleep(2)
print('比较用的')
index() #实际执行的是inner()
copare()
4.9.3 带返回值的修饰器
如果原函数带返回值怎么办?对于前边介绍的几种装饰器实际运行时的index()其实执行的是inner()内部函数,而原来的index含糊的返回值实际上是给了f(),所以如果添加装饰器后,要保持原函数的返回值,就要把f()接受的数值当成inner的返回值,有点绕,代码如下:
import time
# func装饰器
def func(f):
def inner():
start_time = time.time()
s = f()
print(s) #检验:f()实际上执行的是被装饰的原函数,是有返回值的
end_time = time.time()
print(f"执行时间为{end_time - start_time}")
return s
return inner
@func # 等同于 index = func(index)
def index():
time.sleep(2)
print('装饰器')
return 3
@func
def copare():
time.sleep(2)
print('比较用的')
return 4
print(index())
print(copare())
#输出装饰器
3 #f()调用的函数的返回值
执行时间为2.0006275177001953
3
比较用的
4 #f()调用的函数的返回值
执行时间为2.0006816387176514
4如下:
4.9.4带参数的装饰器
import time
# func装饰器
def func(f): #这里的f是被修饰的函数
def inner(*args,**kwargs): #函数的定义,*,**的作用是聚合参数,这里使用动态参数是因为不知道被修饰函数到底有多少参数
start_time = time.time()
s = f(*args,**kwargs) #含糊的执行,*,**的作用是打散参数
end_time = time.time()
print(f"执行时间为{args,kwargs}")
return s #返回被修饰函数的返回值
return inner
@func # 等同于 index = func(index)
def index(argv):
time.sleep(2)
print(argv)
return 3
@func
def copare(argv):
time.sleep(2)
print(argv)
return 4
print(index('haha'))
print(copare('xixi'))
#输出
haha
执行时间为(('haha',), {})
3
xixi
执行时间为(('xixi',), {})
4
4.9.5标准版装饰器
结构如下:
def wrapper(func): #func是被修饰的函数,wrapper是外层函数
def inner(*args,**kwargs): #定义的额内层函数,* **的作用是聚合参数
'''执行被装饰函数之前的操作'''
ret = func #被修饰函数的执行,可以使用* **打散函数
'''执行被装饰函数之后的操作'''
return ret #返回被修饰函数的返回值
return inner #返回被修饰函数
4.9.6 带参数的装饰器***
def outer(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
print('''执行函数之前要做的''')
re = func(*args,**kwargs)
if flag:
print('''执行函数之后要做的''')
return re
return inner
return timer
@outer(False)
def func():
print(111)
func()
4.9.7 多个装饰器修饰一个函数
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()
#输出
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
原则:从上到下再从下到上;从外到内再从内到外
流程如下: