python的装饰器其实就是一个以函数作为参数并返回一个替换函数的可执行函数。
装饰器用到了闭包原理,目的是为了简化代码,用@来表示。
不带参数的装饰器
我们从一个最简单的装饰器例子说起:
def add_one(f):
def fun(*args):
a = args[0]
f(a + 1)
return fun
@add_one
def print_self(a):
print("a =", a)
print_self(1)
程序运行的结果为:
a = 2
为什么参数明明是1,却输出2呢?
关键就在于,因为有了装饰器 @add_one
的作用,print_self
已经不再是原来的 print_self
了。
程序运行时,真正的步骤是:
1、在运行到 print_self(1)
时,先将 print_self
这个函数本身作为参数,传入 add_one
中。
2、然后会返回一个函数 fun
,相当于将 print_self
装饰成了 fun
。
3、然后将参数 1 传给 fun
,于是 args=(1,)
,然后有 f(a+1)
,此时 f
即为 print_self
,因此执行的是 print_self(a+1)
即 print_self(2)
。
这个装饰器,和下面的代码是等效的:
#@add_one
def print_self(a):
print("a =", a)
print_self = add_one(print_self)
print_self(1)
其中,将装饰器@注释掉、且增加了一句手动调用add_one的语句,结果等效。
简化
如果被装饰函数没有参数,则可以将装饰器由闭包改为一个普通的函数,但是也一定要返回一个可调用函数:
def add_one(f):
print('excute add_one')
return f
@add_one
def print_self():
print("excute print_self")
print_self()
结果:
excute add_one
excute print_self
这种形式可以用于记录调用函数的日志。
带参数的装饰器
上面的例子中,@add_one
后面是没有参数的,但是 add_one
函数本身是有一个参数 f
的,装饰器会自动将所装饰的函数 print_self
传给参数 f
。但是有的装饰器是如同 @decorator(a, b)
这样的形式,后面跟着参数,原理又是如何?
这时候,decorator(a, b)
其实不应该当做一个装饰器,而是一个固定的函数值,我们需要计算出 decorator(a, b)
,比如其返回一个函数 return_fun
,那么这个返回的函数才是真正的装饰器,相当于 @retturn_fun
。
记住:@后面跟的永远是一个可调用的函数,而不是一个固定的函数值,如果是一个固定的函数值,那么这个函数值本身,肯定是一个可调用的函数。
举例说明:
改造上面的 add_one
装饰器,我希望add的值不是固定为1,而是可以人为设置的,那么可以将这个加数放到装饰器中,如:
def add_number(num):
def fun(f):
def _fun(*args):
f(args[0] + num)
return _fun
return fun
@add_number(2)
def print_b(b):
print('b =', b)
print_b(1)
运行结果为:
b = 3
运行原理:
1、执行 print_b(1)
时,发现被 @add_number(2)
装饰。
2、计算 add_number(2)
,返回 fun
(此时里面的num
已经固定为2)。
3、将 print_b
当做参数 f
传给 fun
,返回 _fun
。
4、将参数 1
当做参数 *args
传给 _fun
,执行 f(1 + 2)
。
5、输出 b = 3
。
简化
如果被装饰函数没有参数,则可以将装饰器由三层改为两层,但是内层函数一定要返回一个可调用函数,如:
def add_number(num):
def fun(f):
print('num =', num)
return f
return fun
@add_number(2)
def print_hello():
print('hello')
print_hello()
结果:
num = 2
hello