装饰器
1、叠加装饰器
(1)什么是叠加装饰器
在同一个被装饰对象中,添加多个装饰器并执行
(2)为什么要使用叠加装饰器
不使用叠加装饰器会导致代码冗余,结构不清晰,可扩展性差,所以最好每一个新的功能都应该写一个新的装饰器
(3)怎么使用叠加装饰器
使用方式:
@装饰1
@装饰2
@装饰3
def 被装饰对象():
pass
例子1:
我的需求:为被装饰对象,添加统计与登录认证功能
import time
user_info = {
"user": None
}
# 登陆功能
def login():
username = input("请输入您的账户:").strip()
password = input("请输入您的密码:").strip()
with open("dir/passwd.txt", "r", encoding="utf-8") as f: # 我在同级目录下创建了一个dir目录,然后在目录下创建了一个passwd的文本文档,里面内容为tank:123,然后回车另起一行保存
info = f.readline().strip("\n").split(":")
name, pwd = info
if username == name and password == pwd:
print("登陆成功...")
user_info["user"] = username
else:
print("登录失败...")
# 登录认证装饰器
def login_auth(func):
def inner1(*args, **kwargs):
# 登录认证功能
if user_info.get("user"):
res = func(*args, **kwargs)
return res
else:
login()
return func(*args, **kwargs) # 无论inner1中出现任何判断,最后都要返回“调用后的被装饰对象”:func(*args, **kwargs)
return inner1
##### 注意:登陆这里无论inner1中出现任何判断,最后都要返回“调用后的被装饰对象”:func(*args, **kwargs)
# 统计时间装饰器
def time_record(func):
def inner2(*args, **kwargs):
print("开始统计...")
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print(f"消耗时间为{end_time - start_time}")
return res
return inner2
# 装饰顺序:先装饰time_record,再装饰login_auth
@login_auth # inner1 = login_auth(inner2)
@time_record # inner2 = time_record(download_movie)
def download_movie():
print("电影开始下载...")
time.sleep(3)
print("电影下载完成!")
return "gtwz.mp4"
# 执行顺序:先执行login_auth,再执行time_record
# 只统计下载电影的时间
login() # 先调用用户登录,模拟用户已登录
download_movie()
注意:装饰器在调用被装饰对象时才会执行添加的功能
装饰的顺序:由下到上
执行的顺序:由上到下
附上图解:
例子2:通过例子看到执行过程:
def wrapper1(func1):
def inner1(*args, **kwargs):
print("1-----start")
# 被装饰对象在调用时,如果还有其他装饰器,会先执行其他装饰器中的inner
# 下一步执行inner2
res = func1(*args, **kwargs)
print("1-----end")
return res
return inner1
def wrapper2(func2):
def inner2(*args, **kwargs):
print("2-----start")
# 被装饰对象在调用时,如果还有其他装饰器,会先执行其他装饰器中的inner
# 下一步执行inner3
res = func2(*args, **kwargs)
print("2-----end")
return res
return inner2
def wrapper3(func3):
def inner3(*args, **kwargs):
print("3-----start")
# 被装饰对象在调用时,没有其他装饰器
# 下一步执行index
res = func3(*args, **kwargs)
print("3-----end")
return res
return inner3
@wrapper1 # inner1 = wrapper1(inner2)
@wrapper2 # inner2 = wrapper2(inner3)
@wrapper3 # inner3 = wrapper3(index)
# 被装饰对象
def index():
print("hello")
"""
装饰顺序:
从下到上
执行顺序:
从上到下
本例子的执行顺序:
inner1()
inner2()
inner3()
index()
inner3的res
inner2的res
inner1的res
"""
2、无参装饰器和有参装饰器
(1)无参装饰器
在被装饰对象装饰时,没有传参数的装饰器
@wrapper1
@wrapper2
@wrapper3
无参装饰器模板:
# 无参装饰器模板: def wrapper(func): def inner(*args, **kwargs): # 添加代码块功能 res = func(*args, **kwargs) # 添加代码块功能 return res return inner
(2)有参装饰器
有参装饰器:在某些时候,需要给用户的权限进行分类
@wrapper1(参数1)
@wrapper2(参数2)
@wrapper3(参数3)
有参装饰器模板:
# 有参装饰器模板: def outer(para): # 传入的para参数是用在inner()内部函数使用 def wrapper(func): def inner(*args, **kwargs): if para == "": # 功能1 # 添加调用前的功能代码块1 res = func(*args, **kwargs) # 添加调用后的功能代码块1 return res elif para == "": # 功能2 # 添加调用前的功能代码块2 res = func(*args, **kwargs) # 添加调用后的功能代码块2 return res else: # 功能3 # 添加调用前的功能代码块3 res = func(*args, **kwargs) # 添加调用后的功能代码块3 return res # 无论inner中出现任何判断,最后都要返回“调用后的被装饰对象”:func(*args, **kwargs) return inner return wrapper
例1:可通过传参查看用户权限/功能
def user_auth(user_role):
def wrapper(func):
def inner(*args, **kwargs):
if user_role == "SVIP":
# 添加svip的权限/功能
print(user_role)
res = func(*args, **kwargs)
return res
elif user_role == "VIP":
# 添加vip的权限/功能
print(user_role)
res = func(*args, **kwargs)
return res
return inner
return wrapper
@user_auth("VIP")
def index():
pass
index()
3、wraps修复(了解)
wraps是一个修复工具,修复的是被装饰对象的空间
functools import wraps
def wrapper(func):
@wraps(func) # 修改名称空间:inner ---> func
def inner(*args, **kwargs):
"""
这里是装饰器
:param args:
:param kwargs:
:return:
"""
res = func(*args, **kwargs)
return res
return inner
def index():
"""
这里是被装饰对象
:return:
"""
pass
print(index)
print(index.__doc__)