目录
什么是装饰器
装饰器本质上是一个Python函数(其实就是闭包),它可以让其他函数在不需要做任何代码变动的前提下增加额外功能
装饰器能够在那个函数执行前或者执行后分别运行一些代码
装饰器的参数是一个函数,返回值也是一个函数
闭包的相关知识,参考 : Python基础 - 闭包_Melody~M的博客-CSDN博客
添加简单装饰器
有一个test函数,想给test函数执行前后分别添加一些功能(此处用打印功能代替),代码如下:
def my_decorator(func):
def wrapper():
print("func函数调用前")
func()
print("func函数调用后")
return wrapper
def test():
print("test函数")
if __name__ == "__main__":
f = my_decorator(test)
f()
打印结果:
func函数调用前
test函数
func函数调用后
创建一个 my_decorator函数作为装饰器,入参是一个函数,my_decorator内部创建一个闭包wrapper实现添加的功能,my_decorator将闭包函数进行返回
调用时f = my_decorator(test),传入待修饰的函数test,获取wrapper函数的引用并赋值给f
f()则表示执行wrapper函数
语法糖
以上代码,每次调用都需要先赋值再执行(f = my_decorator(test)),较为繁琐
python提供一种更简洁的方式定义装饰器,使用@符号
def my_decorator(func):
def wrapper():
print("func函数调用前")
func()
print("func函数调用后")
return wrapper
@my_decorator
def test():
print("test函数")
if __name__ == "__main__":
test()
使用@my_decorator这种更加简洁的方式来表示:f = my_decorator(test)
带参函数的装饰器:
如果被修饰函数带有参数,那就需要在闭包函数添加此参数,如下面的代码
def my_decorator(func):
def wrapper(x): #添加参数
print("func函数调用前")
func(x) #添加参数
print("func函数调用后")
return wrapper
@my_decorator
def test(x): #添加参数
print("test函数")
if __name__ == "__main__":
test(123)
但是这个写法有一个问题,如果test函数后续有更改,比如参数个数增加,那就需要跟着修改wrapper函数,其他函数想使用这个装饰器也可能需要再去修改wrapper函数
更好的做法应该是wrapper函数不受被修饰函数的影响
def my_decorator(func):
def wrapper(*args, **kwargs): #添加参数
print("func函数调用前")
func(*args, **kwargs) #添加参数
print("func函数调用后")
return wrapper
@my_decorator
def test(x): #添加参数
print("test函数")
if __name__ == "__main__":
test(123)
将wrapper的参数写成*args, **kwargs形式,利用Python的参数解包,传入任意形式的参数都可以被解析
带参数的装饰器:
希望有一个装饰器,接受一个参数num, 输入num是几就执行几次
实现如下:
def my_decorator_repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs): #添加参数
for i in range(num):
print("func函数调用前")
func(*args, **kwargs) #添加参数
print("func函数调用后")
return wrapper
return my_decorator
@my_decorator_repeat(num = 2)
def test(x): #添加参数
print(x)
if __name__ == "__main__":
test(123)
#输出
func函数调用前
123
func函数调用后
func函数调用前
123
func函数调用后
在原来的装饰器函数外,再套一层函数,用于传入num值
wrapper函数可以访问此num,对其进行遍历
最外层函数将原来的装饰器函数引用返回,return my_decorator
通过语法糖@my_decorator_repeat(num = 2),获取到my_decorator装饰器
多个装饰器
def my_decorator1(func):
print("my_decorator1")
def wrapper():
print("my_decorator1调用前")
func()
print("my_decorator1调用后")
return wrapper
def my_decorator2(func):
print("my_decorator2")
def wrapper():
print("my_decorator2调用前")
func()
print("my_decorator2调用后")
return wrapper
@my_decorator1
@my_decorator2
def test():
print("test函数")
if __name__ == "__main__":
test()
执行结果如下:
my_decorator2
my_decorator1
my_decorator1调用前
my_decorator2调用前
test函数
my_decorator2调用后
my_decorator1调用后
1、主代码运行后,依次运行@my_decorator1,@my_decorator2
2、运行到@my_decorator1,发现是一个装饰器,因此寻找my_decorator1的func函数,但@my_decorator1后面是@my_decorator2,因此开始执行@my_decorator2
3、执行@my_decorator2,寻找my_decorator2的func函数,@my_decorator2后面是test()函数,传入test函数执行my_decorator2,打印出“my_decorator2”,然后返回my_decorator2中的wrapper
4、传入test函数执行my_decorator1,打印“my_decorator1”,然后返回my_decorator1中的wrapper
类装饰器
装饰器函数必须接受一个callable对象作为参数,然后返回一个callable对象
python中,一般callable对象都是函数,但是也有例外,比如某个对象重写了call方法,这个对象也是callable的
class Test(object):
def __init__(self, func):
self.func = func
def my_decorator(self):
print("my_decorator")
def __call__(self, *args, **kwargs):
self.my_decorator()
self.func()
@Test
def test():
print('this is test func')
test()
执行结果如下:
my_decorator
this is test func
1、执行到@Test, 将test()函数传入Test对象,调用init方法
2、将test()指向创建的Test类对象,执行test()时,就会执行Test类对象的__call__方法