装饰器是python提供的一个重要特性,使用装饰器,可以允许修改其他函数功能.
什么是装饰器
上边已经提到,装饰器可以修改其他函数的功能,使代码更简短.
为什么要使用装饰器
现在有一个函数test(x,y)
,作用是计算x+y
def test(x, y):
return x + y
现在需要对test()
的结果,再乘2,简单点的做法.
def test(x, y):
return x + y
print(test(1,2) * 2)
如果实际项目中多次调用了test()
函数,那么需要多个地方修改.于是就想到了另外一种方法,直接修改test()
def test(x, y):
return (x + y) * 2
没有问题,对于简单的问题,可以这样修改,但实际项目中,test()
很可能是前辈留下的屎山,最好不要取碰触.
有没有不需要修改原始函数,又能完成新增功能的方法呢? 骚年,使用装饰器吧
定义一个装饰器
先来一个简单的例子,现在有一个方法,没有参数,没有返回值,就输出一段信息,如下:
def a():
print("hello")
现在需要在执行函数后自动补全world,利用装饰器来完成,代码如下.
def decorator(func):
def inner_func():
func()
print("world")
return inner_func
@decorator
def a():
print("hello")
a()
来看装饰器的写法,首先定一个函数decorator()
,参数名为func
,是一个函数类型.
然后在这个函数内部再定义一个函数inner_func()
.在inner_func()
中调用func()
,然后再增加需要的功能,最后把inner_func
当作返回值返回.
然后在被装饰函数a()
上边用@+函数名
来指定装饰函数,最后调用a()
执行,实际执行的是装饰函数.
装饰器的本质
装饰器本质上就是一个闭包函数,下面来分解这个例子.
python中的一切都是对象,函数也是对象,那么也就可以进行赋值操作.
def inner_func():
print("hello")
print("world")
a = inner_func
a()
这么看起来可能不容易理解,那就再来一层
def decorator():
def inner_func():
print("hello")
print("world")
return inner_func
a = decorator()
a()
发现了么,装饰器实际的作用就是将装饰函数内部的闭包函数赋值给被装饰函数.
有参数有返回值的装饰函数
不是所有的函数均和上述例子一样简单,实际使用过程中会有参数,返回值.
这种情况下,就要遵循一下规则
- 被装饰函数有参数,装饰函数内部的闭包函数也需要有参数,并且顺序数量与被装饰函数一致
- 被装饰函数有返回值,闭包函数也需要有返回值,但返回值类型不限.
回到最开始的例子,用装饰器来改写一下.
def dec(func):
def wrap(x, y):
return func(x, y) * 2
return wrap
@dec
def test(x, y):
return x + y
print(test(1, 2))
wrap()
参数与test()
保持一致,并且有return
执行时机
了解了上述,那么装饰器是什么时机执行的,顺序是什么就很重要,在上面的例子上加一点输出.
def dec(func):
print("进入装饰函数dec()")
def wrap(x, y):
print("进入内部函数wrap()")
return func(x, y) * 2
print("返回内部函数wrap()")
return wrap
@dec
def test(x, y):
return x + y
此时没有调用,直接运行,输出
进入装饰函数dec()
返回内部函数wrap()
可以看到,在执行前,test()
就已经被装饰器替换了.对于外部模块,装饰器模块是在何时运行的?
将上例保存成t.py文件.新建一个python文件,import上例模块
print("导入模块之前")
import t
print("导入模块")
结果
导入模块之前
进入装饰函数dec()
返回内部函数wrap()
导入模块
可以发现,在加载模块的时候,装饰器就已经发生作用了,现在来看一下执行顺序.
def dec(func):
print("进入装饰函数dec()")
def wrap(x, y):
print("进入内部函数wrap()")
return func(x, y) * 2
print("返回内部函数wrap()")
return wrap
@dec
def test(x, y):
return x + y
print("准备运行test()函数")
print("test()运行结果", test(1, 2))
print("test()函数运行完毕")
结果
进入装饰函数dec()
返回内部函数wrap()
准备运行test()函数
进入内部函数wrap()
test()运行结果 6
test()函数运行完毕
在调用被修饰函数时,才会取执行修饰函数内部的闭包函数.