装饰器
一、什么是装饰器
器:指的是工具,可以定义成函数
装饰:指的是被装饰对象添加新的工功能
装饰器:指的是定义一个函数,所定义的这个函数是用来为其他函数添加新的功能
装饰器的的实现原理:函数嵌套+闭包+函数对象的组合使用的产物
二、为什么需要装饰器
软件的涉及应该遵循开发封闭原则
开放:指的是对扩展功能是开放的
封闭:指的是对修改源代码是封闭的
用处:(为遵循开放封闭远程)在不修改源代码的情况下,需要对该功能进扩展,增加新的功能,这时就要用到装饰器
例如:当一个软件已经发布使用后,并且运行很正常,如果需要增加新的功能(扩展该功能),这时如果在源代码的基础上进行修改增加功能后,有可能会出现想象不到的bug
这个时候就不要去在源代码上进行增加功能了,就用到装饰器,在定义一个新的函数,定义的新的函数就是为其原来函数添加新的功能
三、实现装饰器的过程
def index(x,y):
time.sleep(2) # 等待2s执行下面的代码
print(x,y)
index(111,222)
在不修改源代码的和调用方式的情况下,给该功能添加功能,计算代码的执行时间
实现方式一:实现了计算执行代码的时间,没有修改调用方式,但是修改了源代码
import time # 导入一个函数,时间函数
def index(x,y):
start=time.time() # 记录开始时间,time.time()是记录1970到现在的时间
time.sleep(2) # 等待2s在执行下面的函数
print(x,y)
stop=time.time() # 记录结束时间
print(stop-start) # 计算执行的秒数
index(111,222)
实现方式二:没有修改源代码,也没有修改调用方式,但是代码“冗余”了,当有其他很多地方都调用了index函数时,就会出现重复代码
import time
def index(x,y):
time.sleep(2)
print(x,y)
start=time.time()
index(111,222)
stop = time.time()
print(stop-start)
实现方式三:解决了代码“冗余”,没有对源代码进行修改,但是修改了调用方式,并且把index函数写死了,只能对index函数进行调用了
import time
def index(x,y):
time.sleep(2)
print(x,y)
def ouuter(*args,**kwargs):
start=time.time()
index(*args,**kwargs)
stop = time.time()
print(stop-start)
outer(111,222)
实现方式四:使用闭包函数方式实现,没有修改源代码,也没有修改函数的调用方式,把index写活了,当遇到其他的函数也需要添加该功能时也可以直接进行调用
import time
def index(x,y):
time.sleep(2)
print(x,y)
def timer(func) # func = index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs) # 把index换成了一个变量
stop = time.time()
print(stop-start)
retrun wrapper
index = timer(index)
index(111,222)
# 如果还有其他函数也需要增加统计时间的功能,如有home函数
def home(a,b,c):
pritn(a,b,c)
home=timer(home)
home(3333.444.5555)
实现方式五:如果该所调用的函数存在返回值,在方式四的基础上,为嵌套函数中的调用进行赋值,然后在进行返回
def index(x,y):
time.sleep(2)
print(x,y)
return 123
def timer(func):
def wrapper(*args,**kwargs):
start=time.time()
res = func(*args,**kwargs) # 给func函数调用赋值为res
stop=time.time()
print(stop-start)
return res # 返回res
return wrapper
index = timer(index)
res = index(111,222)
print(res)
注意:实现方式五完全实现了在不修改源代码和调用方式的情况下给函数index增加一个执行时间的统计功能
问题:但是在其他函数也需要用到这个功能的时候就需要重新进行如home = timer(home)的方式
实现方式六:在以后函数需要调用装饰器时,不用在进行index = timer(index)
def timer(func):
def wrapper(*args,**kwargs):
start=time.time()
res = func(*args,**kwargs) # 给func函数调用赋值为res
stop=time.time()
print(stop-start)
return res # 返回res
return wrapper
@timer # 相当于 index = timer(index)
def index(x,y):
time.sleep(2)
print(x,y)
return 123
res = index(111,222)
print(res)
注意:在需要调用装饰器的上面加上@装饰器名字,表示一下函数调用该装饰器,但是装饰器必须写在调用之前
四、总结实现方式五和六,以后在为被装饰对象添加新功能时,可以进行用到装饰器
装饰器方式是:
def timer(func):
def wrapper(*args,**kwargs):
`````````` # 中间为需要实现的代码
res = func(*args,**kwargs)
return res
return wrapper
五、在使用装饰器后,当去查看原函数名或注释时,这里输出的为装饰器的函数名而不是原函数的名称或显示的注释,这是需要把装饰器和原函数伪造的更像需要加上functools
示例如下 def auth(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
@auth
def handler():
pass
handler()
print(handler.__name__) # inner
如上示例代码,当查handler的函数名时,其实看到的为inner函数(装饰器中的函数),这时需要把装饰器中的函数和原函数伪装的更像
如下示例:
import functools
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
@auth
def handler():
pass
handler()
print(handler.__name__) # handler
加上@functools.wraps(func)后,查看函数名或注释时都是现实为原函数,注意在以后的代码中,装饰器都加上该行代码
总结:结合上面四和五的示例代码,在以后写装饰器时都进行如下格式:
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
"""巴巴里吧""" # 注释
res = func(*args, **kwargs) # 执行原函数
return res
return inner
浙公网安备 33010602011771号