第二十九篇 装饰器
一、无参装饰器
1.什么是装饰器
- 1.修改或增加其他函数的功能的函数
- 2.需要注意的是:
- 1.装饰器本身是可以被任意调用的对象
- 2.装饰的对象也可以是任意可调用的对象
2.为什么需要装饰器
当我们需要修改程序的某个功能,但是不想修改函数或对象的调用方法时,这时就可以使用装饰器。由于软件的维护需要遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能是开放的
3.如何使用装饰器
-
1.装饰器的实现必须遵循两大原则(装饰器就是在遵循以下两个原则的前提下为被装饰对象添加新功能):
# 假设源码是一个延时函数 import time def timesleep(): time.sleep(1) print('睡眠一秒')
- 1.不修改被装饰对象的源代码
# 改源码增加功能 import time def counttime(): start = time.time() time.sleep(1) print('睡眠一秒') end = time.tiem() pirnt(end-start)
- 2.不修改被装饰对象的调用方法
# 改变调用方法 import time def timesleep(): time.sleep(1) print('睡眠一秒') def counttime(): start = time.time() timesleep() end = time.tiem() pirnt(end-start)
-
利用装饰器实现
# 源码
import time
def timesleep():
time.sleep(1)
print('睡眠一秒')
# 装饰器
def deco(func):
def wrapper():
start = time.time()
func()
end = time.tiem()
pirnt(end-start)
return wrapper
# 定义变量接收返回值
timesleep = deco(timesleep)
timesleep()
4.完善装饰器
- 上述的装饰器,最后调用timesleep()的时候,其实是在调用wrapper(),因此如果原始的timesleep()有返回值的时候,wrapper()函数的返回值应该和timesleep()的返回值相同,也就是说,我们需要同步原始的timesleep()和wrapper()的返回值
# 源码
import time
def timesleep():
time.sleep(1)
print('睡眠一秒')
return 666
# 装饰器
def deco(func):
def wrapper():
start = time.time()
res = func()
end = time.tiem()
pirnt(end-start)
return res
return wrapper
# 定义变量接收返回值
timesleep = deco(timesleep)
res = timesleep()
print(res)
- 如果原始的timesleep()函数需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper() = timesleep(),所以给wrapper()函数传参即可
# 源码
import time
def timesleep(*args,**kwargs):
time.sleep(1)
print('睡眠一秒')
print(args,kwargs)
return 666
# 装饰器
def deco(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.tiem()
pirnt(end-start)
return res
return wrapper
# 定义变量接收返回值
timesleep = deco(timesleep)
res = timesleep(2,3,3,66,name='king',age=20)
print(res)
5.装饰器语法糖
- 在被装饰函数正上方,并且是单独一行写上
@装饰器名
# 源码
import time
# 装饰器
def deco(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.tiem()
pirnt(end-start)
return res
return wrapper
@deco
def timesleep(*args,**kwargs):
time.sleep(1)
print('睡眠一秒')
print(args,kwargs)
return 666
res = timesleep(2,3,3,66,name='king',age=20)
print(res)
6.装饰器模板
def deco(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
二、有参装饰器
1.三层闭包
- 由于两层的装饰器,参数必须得固定为 func ,但三层的装饰器解除了这个限制,我们不仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也即是说,三层装饰器已经够用,在多一层已是多余
is_login_dic = {'username':None}
def auth(origin):
def login_deco(func):
def wrapper(*args,**kwargs):
if origin == 'file':
if not is_login_dic['username']:
username = input('请输入注册用户名:').strip()
if username != 'king':
print('非法登陆')
return
is_login_dic['username'] = username
res = func(*args,**kwargs)
return res
else:
res = func(*args,**kwargs)
return res
elif origin == 'mysql':
print('非法登陆')
else:
print('登陆失败')
return wrapper
return login_deco
# f = origin('file')
# shopping = f(shopping)
# shopping()
@auth('file')
def shopping():
print('欢迎光临')
@auth('mysql')
def withdraw():
print('欢迎提现')