装饰器
装饰器
定义
装饰的工具
必须要遵循的原则:“开放封闭”
开放:对源函数功能的添加是开放的
封闭:对源函数功能修改是封闭的
装饰器的作用
在不修改被装饰对象源代码和调用方式的前提下,增加新功能
必须遵循的原则:
1、不修改被装饰对象的源代码
2、不修改被装饰对象的调用方式
为什么要使用装饰器
解决代码冗余问题,提高代码可扩展性
怎么使用装饰器
编写装饰器通过闭包函数实现
装饰器推导过程
# # 装饰器推导过程
def move():
'''下载电影的源函数'''
print('开始下载')
time.sleep(2)
print('下载完成')
def fun(index):
def down():
star_time = time.time()
index() # 将被装饰对象move赋值给index执行
end_time = time.time()
print(f'总耗时{end_time - star_time}')
return down
fun(move)()
# 有返回值时
def move():
'''下载电影的源函数'''
print('开始下载')
time.sleep(2)
print('下载完成')
return '明日之巅.mp4'
def fun(index):
def down():
star_time = time.time()
res = index() # 将index执行结果赋值给res
# print(res)
end_time = time.time()
print(f'总耗时{end_time - star_time}')
return res # 将index返回值返回出来
# print(down())
return down
move = fun(move) # 将调用方式改成被装饰对象
move()
# 源函数有参数时
def move(url):
'''下载电影的源函数'''
print(f'{url}开始下载')
time.sleep(2)
print('下载完成')
return '明日之巅.mp4'
def wrapper(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print(f'总耗时:{end_time - start_time}')
return res
return inner
move = wrapper(move)
move(('https://www.cnblogs.com'))
装饰器最终模板
def wrapper(func):
def inner(*args, **kwargs):
# 调用前增加新功能
res = func(*args, **kwargs) # 调用被装饰对象,接收返回值
# 调用后增加的新功能
return res # 接收被装饰对象的返回值
return inner
装饰器语法糖
1、装饰器的语法糖是属于装饰器的
2、用@+装饰器名,在被装饰对象开头
3、在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上
# 增加统计代码运行时间的装饰器
def wrapper(func):
def inner(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
end_time = time.time()
print(end_time-start_time)
return res
return inner
# 使用语法糖
@wrapper
def movie():
print("开始下载")
time.sleep(2)
print('下载结束')
# 不使用语法糖调用增加新功能后的函数
# movie = wrapper(movie)
# movie()
# 使用语法糖后可直接调用
movie()
叠加装饰器
在同一个被装饰对象中,添加多个装饰器,并执行
每一个新功能都写一个新的装饰器,否则会导致代码冗余,结构不清晰,可扩展性差
@装饰器1
@装饰器2
@装饰器3
def 被装饰对象():
pass
注意:在调用被装饰对象时,才会执行添加的新功能
叠加装饰器装饰顺序,执行顺序:
装饰的顺序:由下往上装饰
执行的顺序:由上往下执行
注意:
1、装饰器内函数inner中出现任何判断,最后都要返回“调用后的被装饰对象”func(*args,**kwargs)
2、被装饰对象在调用时,如果还有其他装饰器,会先执行其他装饰器中的inner
def wrapper1(func1):
def inner1(*args, **kwargs):
print("1————start")
res = func1(*args, **kwargs)
print("1————end")
return res
return inner1
def wrapper2(func2):
def inner2(*args, **kwargs):
print("2————start")
res = func2(*args, **kwargs)
print("2————end")
return res
return inner2
def wrapper3(func3):
def inner3(*args, **kwargs):
print("3————start")
res = func3(*args, **kwargs)
print("3————end")
return res
return inner3
@wrapper1
@wrapper2
@wrapper3
def index():
print("index")
index()
>>>
# 执行顺序:由上而下
# 装饰顺序:由下而上
1————start
2————start
3————start
index
3————end
2————end
1————end
有参装饰器
无参装饰器:装饰在被装饰对象时,没有传参数的装饰器
# 以下是无参装饰器
@wrapper1
@wrapper2
@wrapper3
有参装饰器:在某些时候我们需要给用户的权限进行分类
# 以下是有参装饰器
@wrapper1(参数1)
@wrapper2(参数2)
@wrapper3(参数3)
如果我们想提供多种不同的认证方式以供选择 , 函数inner需要一个driver参数,而函数wrapper与inner的参数都有其特定的功能,不能用来接受其他类别的参数,可以在wrapper的外部再包一层函数user_auth,用来专门接受额外的参数,这样便保证了在user_auth函数内无论多少层都可以引用到
# 给用户的权限进行分类
def user_auth(driver):
def wrapper(func):
def inner(*args, **kwargs):
if driver == "svip":
# 加入超级会员的功能
res = func(*args, **kwargs)
return res
elif driver == "vip":
# 加入会员的功能
res = func(*args, **kwargs)
return res
else:
# 加入普通用户的功能
res = func(*args, **kwargs)
return res
return inner
return wrapper
@user_auth(driver='svip')
def user_power():
pass
user_power()
wraps
是一个修复工具,修复的是被装饰对象的空间
使用:
1、先从functools中调用wraps >>> from functools import wraps
2、装饰器在定义内层函数前加@wraps(func) 修改名称空间:将inner >>> func
3、inner前加@wraps(func)是将inner名称空间修改到func,如果不用@wraps(func)打印注释本质打印的是inner的注释,加上@wraps(func)修复被装饰对象的空间,打印注释就会打印被装饰对象的注释
# 函数对象.__doc__ 是查看内部的注释
from functools import wraps
# 从functools中调用wraps模块
def wrapper(func):
@wraps(func) # 修改名称空间,inner >>> func
def inner(*args,**kwargs):
'''
装饰器的注释
'''
# 调用被装饰对象前加的功能
res = func(*args,**kwargs)
# 调用被装饰对象前加的功能
return res
return inner
@wrapper
def index():
'''
被装饰对象index的注释
:return:
'''
pass
print(index.__doc__)
# 加上@wraps(func)
>>>
被装饰对象index的注释
:return:
# 不加@wraps(func)
>>>
装饰器的注释