装饰器(Decorator)
装饰器
1.如果要增强一个函数的功能,但又不希望更改原函数中的代码,这种在代码运行期间动态增加功能的机制被称为装饰器
【Decorator】
2. 本质:实际上就是一个闭包,只不过被装饰的函数需要作为参数传递给闭包
3.装饰器的书写格式:给闭包的外部函数设置一个参数【需要被装饰的函数】,外部函数的返回值是内部函数的引用【装饰的结果】,这种函数被称为高阶函数
4.装饰器的好处
① 不用修改源代码就可以更改函数
② 提高代码的维护性
③ 提高代码的复用性
5.应用场景:
① 引入日志
② 统计函数的执行时间
③权限校验
④缓存
一、案例1:股票交易(买入、卖出)
版本1:
-
简单的买入、卖出
# encoding: utf-8
# @File : test01.py
# @Author: wu shaofan
# @Desc : 股票买入、卖出
# @Date : 2024/02/08
def buy():
print("买入股票成功")
def sell():
print("卖出股票成功")
if __name__ == '__main__':
buy()
sell()
买入股票成功
卖出股票成功
Process finished with exit code 0
版本2:
-
想买入或者卖出之前,输入密码验证
-
# encoding: utf-8 # @File : test02.py # @Author: wu shaofan # @Desc : 卖出、买入股票需要输入密码 # @Date : 2024/02/08 def buy(): password = input("请输入密码:") if password == "123456": print("买入股票成功!") else: print("密码错误,买入股票失败!") def sell(): password = input("请输入密码:") if password == "123456": print("卖出股票成功!") else: print("密码错误,卖出股票失败!") if __name__ == '__main__': buy() sell()
请输入密码:123456 买入股票成功! 请输入密码:123456 卖出股票成功! Process finished with exit code 0
缺点:
功能有了,不过buy()和sell()方法变复杂了,且每个函数中有重复的代码,如果buy()和sell()方法是别人写的,功能已经完善了,最好不要改动代码
版本3:
-
重新定义一个方法
# encoding: utf-8 # @File : test03.py # @Author: wu shaofan # @Desc : # @Date : 2024/02/08 def buy(): print("买入股票成功") def sell(): print("卖出股票成功") def pwd(order): password = input("请输入密码:") if password == "123456": order() else: print("密码错误,请重新输入!") if __name__ == '__main__': pwd(buy) pwd(sell)
请输入密码:123456 买入股票成功 请输入密码:123456 卖出股票成功 Process finished with exit code 0
期望:买入、卖出还是就执行buy和sell方法
版本4:装饰器一般写法
# encoding: utf-8
# @File : test04.py
# @Author: wu shaofan
# @Desc :
# @Date : 2024/02/08
def buy():
print("买入股票成功")
def sell():
print("卖出股票成功")
def pwd(order):
def order_new():
password = input("请输入密码:")
if password == "123456":
order()
else:
print("密码错误,请重新输入!")
return order_new
if __name__ == '__main__':
buy = pwd(buy)
buy()
请输入密码:123456
买入股票成功
Process finished with exit code 0
版本5:标准的语法糖写法
功能已经实现,不过python提供了更好的方法
python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器
( @pwd 等于buy = pwd(buy) )
# encoding: utf-8
# @File : test04.py
# @Author: wu shaofan
# @Desc :
# @Date : 2024/02/08
def pwd(order):
def order_new():
password = input("请输入密码:")
if password == "123456":
order()
else:
print("密码错误,请重新输入!")
return order_new
#TODO @pwd 等价于 buy = pwd(buy)
@pwd
def buy():
print("买入股票成功")
@pwd
def sell():
print("卖出股票成功")
buy()
请输入密码:123456
买入股票成功
Process finished with exit code 0
版本6:被装饰函数有返回值
# encoding: utf-8
# @File : test04.py
# @Author: wu shaofan
# @Desc : 被装饰函数有返回值
# @Date : 2024/02/08
def pwd(order):
def order_new():
password = input("请输入密码:")
r = None # 初始化 r 为 None,不初始化也没问题,不过根据python语法规则会有警告
if password == "123456":
r = order()
else:
print("密码错误,请重新输入!")
return r
return order_new
@pwd
def buy():
print("买入股票成功")
return "买入666"
@pwd
def sell():
print("卖出股票成功")
return "卖出666"
obj = buy()
print(obj)
请输入密码:123456
买入股票成功
买入666
Process finished with exit code 0
版本7:被装饰函数带参数
# encoding: utf-8
# @File : test04_1.py
# @Author: wu shaofan
# @Date : 2024/02/09
# @Desc : 被装饰函数带参数
def pwd(order):
def order_new(*args,**kwargs):
password = input("请输入密码:")
r = None # 初始化 r 为 None,不初始化也没问题,不过根据python语法规则会有警告
if password == "123456":
r = order(*args,**kwargs)
else:
print("密码错误,请重新输入!")
return r
return order_new
@pwd
def buy(name):
print("{}买入股票成功".format(name))
return "买入666"
@pwd
def sell(age,name):
print("{}岁的{}卖出股票成功".format(age,name))
return "卖出666"
obj = buy("吴彦祖")
print(obj)
obj = sell(18,"吴彦祖")
print(obj)
请输入密码:123456
吴彦祖买入股票成功
买入666
请输入密码:123456
18岁的吴彦祖卖出股票成功
卖出666
Process finished with exit code 0
版本8:被装饰器函数带参数进阶版
装饰器也带参数
# encoding: utf-8
# @File : test04_02.py
# @Author: wu shaofan
# @Date : 2024/02/09
# @Desc : 被装饰器带参数进阶版
def pwd(type):
def order_new(order):
def infunc(*args,**kwargs):
password = input("请输入密码:")
r = None # 初始化 r 为 None,不初始化也没问题,不过根据python语法规则会有警告
if password == "123456" and type == "买入":
r = order(*args,**kwargs)
elif password == "123456" and type == "卖出":
r = order(*args,**kwargs)
else:
print("密码错误,请重新输入!")
return r
return infunc
return order_new
@pwd("买入")
def buy(name):
print("{}买入股票成功".format(name))
return "买入666"
@pwd("卖出")
def sell(age,name):
print("{}岁的{}卖出股票成功".format(age,name))
return "卖出666"
obj = buy("吴彦祖")
print(obj)
obj = sell(18,"吴彦祖")
print(obj)
请输入密码:123456
吴彦祖买入股票成功
买入666
请输入密码:123456
18岁的吴彦祖卖出股票成功
卖出666
Process finished with exit code 0
总结:标准版的装饰器
标准版的装饰器;
def wrapper(f):
def inner(*args,**kwargs): # 函数的定义:* 聚合 args = ('李舒淇',18)
'''添加额外的功能:执行被装饰函数之前的操作'''
ret = f(*args,**kwargs) #* 打散:f(*args) --> f(*('李舒淇',18)) --> f('李舒淇',18)
''''添加额外的功能:执行被装饰函数之后的操作'''
return ret
return inner
二、装饰器进阶
1、类装饰器
类装饰器这个写法,主要思路就是返回一个增加了新功能的函数对象,只不过这个函数对象是一个类的实例对象。由于装饰器是可调用对象,所以必须在类里面实现call方法,这样由类生成的各种实例加上()就可以运行了。
1.1、不带参数的类装饰器
# encoding: utf-8
# @File : test11.py
# @Author: wu shaofan
# @Date : 2024/02/10
# @Desc : 不带参数的类装饰器
class Decorator:
def __init__(self, func):
self.func = func
def start(self):
print("此方法在函数运行之前被调用")
def end(self):
print("此方法在函数运行结束之后被调用")
def __call__(self, *args, **kwargs):
self.start()
self.func()
self.end()
# f1 = Decorator(f1)
@Decorator
def f1():
print("我是被装饰函数")
f1()
##########################################
此方法在函数运行之前被调用
我是被装饰函数
此方法在函数运行结束之后被调用
Process finished with exit code 0
过程分析:
拓展:call()方法介绍
2,带参数的类装饰器
import time
class Decorator:
def __init__(self, func):
self.func = func
def defer_time(self,time_sec):
time.sleep(time_sec)
print(f"{time_sec}s延时结束了")
def __call__(self, time):
self.defer_time(time)
self.func()
@Decorator
def f1():
print("延时之后我才开始执行")
f1(5)