[python] 之 装饰器
装饰器本质上是一个python函数,它可以让其它函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。装饰器主要用于插入日志、性能测试、事务处理、缓存及权限校验等,解决代码重复使用。值得注意的是,内层函数保留(记忆)了外层函数的(参数)状态。
一、装饰器创建
装饰器的语法以@开头,接着是装饰器函数的变量名和可选参数,紧接着是被修饰函数的定义及被修饰函数的可选参数。
语法:
@decorator(dec_opt_args)
def fuc2Bdecorated(func_opt_args):
func_body
1 def dec1(func1): 2 def wrapper1(): 3 print('hello!') 4 return func1 5 return wrapper1 6 @dec1 7 def test(): 8 pass 9 10 test() 11 #输出 12 hello!
装饰器可以像函数一样堆叠起来,如下:
1 def dec1(func1): 2 def wrapper1(): 3 print('hello!') 4 return func1 5 return wrapper1 6 7 def dec2(func2): 8 def wrapper2(): 9 print ('Pyhton!') 10 return func2 11 return wrapper2 12 13 @dec2 14 @dec1 15 def test(): 16 pass 17 if __name__ == '__main__': 18 test() 19 #输出 20 Python!
此时,函数变量名test作为一个参数传递给了装饰器函数dec1,并返回wrapper1;然后wrapper1作为参数传递给了dec2,并返回了wrapper2;最后test()等价于wrapper2()的调用,即dec2(dec1(test()))。注意,如果装饰器函数有参数,应现执行装饰器函数,并返回一个可调用函数,继续遵循上述的运算规则;如果被修饰的函数有参数,则作为最后返回的可调用的函数的参数,进行最后的运算,并返回值,执行结束。(如下)。
二、有参数的函数装饰器
2.1 被修饰的函数含有参数
该函数等价于dec2(dec1(test('dec00')))
1 def dec1(func1): #func = test 2 def wrapper1(arg1): # arg1 = arg2 3 print(arg1+' + hello!') 4 return func1(arg1) #func1 = test 5 return wrapper1 6 7 def dec2(func2): #func2 = wrapper1 8 def wrapper2(arg2): # arg2 = 'dec00' 9 print (arg2+' + Pyhton!') 10 return func2(arg2) #wrapper1(arg2) 11 return wrapper2 12 13 @dec2 14 @dec1 15 def test(t): 16 print(t+' + test') 17 if __name__ == '__main__': 18 test('dec00') 19 20 #输出 21 dec00 + Pyhton! 22 dec00 + hello! 23 dec00 + test
2.2 装饰器函数含有参数
该函数首先执行dec2('dec2'),返回dec21,然后整个函数等价于dec21(dec1(test01('dec01')))或者dec2('dec2')(dec1(test01('dec01'))),请读者认真体会以上两种情况。
1 def dec1(func1): #func = test01 2 def wrapper1(arg1): 3 print(arg1+' + hello!') 4 return func1(arg1) 5 return wrapper1 6 7 def dec2(level): #level = 'dec2' 8 def dec21(func2): #func2 = wrapper1 9 def wrapper2(arg2): #arg2 = 'dec01' 10 print (level+' + '+arg2+' + Pyhton!') 11 return func2(arg2) #func2 = wrapper1 12 return wrapper2 13 return dec21 14 @dec2('dec2') 15 @dec1 16 def test01(t): 17 print(t +' + test01') 18 if __name__ == "__main__": 19 test01('dec01') 20 #输出 21 dec2 + dec01 + Pyhton! 22 dec01 + hello! 23 dec01 + test01
三、基于类的装饰器
根据装饰器语法及其运算性质,可知装饰器函数本身必须是可调用(callable)的,然后返回一个可调用(callable)对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()
方法,那么这个对象就是callable的。
下面是可调用的类:
1 class P(object): 2 def __call__(self): 3 print ('callable') 4 def func(self): 5 print ('class') 6 7 i = P() 8 i.func() 9 i() 10 11 #输出 12 class 13 callable
3.1 基于可调用的类,创建装饰器:
1 class P(object): 2 def __init__(self,func): 3 self.func = func 4 def __call__(self): 5 print ('callable') 6 return self.func() 7 @P 8 def test(): 9 print('test') 10 if __name__ == "__main__" : 11 test() 12 #输出 13 callable 14 test
在上述函数调用过程中,类P是一个可调用的对象,类似于函数。上述装饰器执行等价于P(test())。
3.2 带参数的类装饰器
1 class P(object): 2 def __init__(self,level): 3 self.level = level # level = 'level' 4 def __call__(self,func): # func = test 5 def wrapper(t): #t = 't' 6 print (self.level + ' + callable') 7 return func(t) # test('t') 8 return wrapper 9 @P('level') 10 def test(t): 11 print('test') 12 if __name__ == "__main__" : 13 test('t') 14 15 #输出 16 level + callable 17 test
此装饰器执行时,首先调用类P,初始化类并返回了函数变量名wrapper,此时该函数的参数,来源与被修饰的函数的参数,这一点和基于函数的装饰器是等同的。