1. python的decorator语法有两种等价形式,分别如下:
第一种形式
@dec2
@dec1
def func(arg1, arg2, ...):
pass
This is equivalent to:
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
第二种形式:
@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
pass
This is equivalent to:
func = decomaker(argA, argB, ...)(func)
2. 本文主要是介绍一种比较优雅的python decorator的使用方式
即对于一个decorator即要能够给予参数,又能不给予参数.如果使用它的第二种形式则对于没有参数时也必须用@decomaker()这种形式,通过本文的这种方法可以实现对于没有参数时使用@decomaker即可,对于有参数时使用@decomaker(argA,argB,..)的形式使用.解决这个问题的方法是对与decorator再使用一层decorator.对于被修饰的decorator有无参数返回两种不同的函数,从而实现该功能.即使用一个decoratordecorator函数修饰decorator函数,使得它在被修饰函数按等价语法调用decorator函数时,根据它的参数个数生成不同函数,从而实现该功能. 对于decorator被采用无参数的格式调用是,它的等价形式是被修饰的函数f应该等价于decorator(f),所以此时的decoratordecorator应该返回decorator(f).对于decoartor采用@decoartor(argA,argB,..)调用时,此时的decoratordecorator应该返回decorator(argA,argB,..)(f).对于这种连着的调用函数的形式我们可以使用闭包来实现,从左到右的括号内的参数对应则从里到外的函数的实参.decoratordecorator对于decorator的修饰肯定是用无参的形式.即等价形式是decorator=decoratordecorator(decorator)具体的代码如下:
def wrapper(function):
def fun(*args, **kwargs):
print('in wrapper, you add to do something')
return function(*args, **kwargs)
return fun
@wrapper
def decoratored(*args, **kwargs):
print('in fun, args is {0}, kwargs is {1}'.format(str(args), str(kwargs)))
def test_wrapper():
decoratored(1,2, a=2)
def double_wrapper(function):
def decorator(*args, **kwargs):
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return function(args[0])
else:
def fn( decoratedFn):
# print('in double_wrapper fn decorator, args is {0}, kwargs is {1}'.format(str(args1), str(kwargs1)))
return function(decoratedFn, *args, **kwargs)
return fn
return decorator
@double_wrapper
def first_double_wrapper(function, *args, **kwargs):
def decorator(*args1, **kwargs2):
print('in first_double_wrapper decorator, args is {0}, kwargs is {1}'.format(str(args), str(kwargs)))
print('in first_double_wrapper decorator, args is {0}, kwargs is {1}'.format(str(args1), str(kwargs2)))
return function(*args1, **kwargs2)
return decorator
def first_wrapper(*args, **kwargs):
def decorator(function):
print('this is execute only once')
def func(*args1, **kwargs1):
print('wrapper argments is ' + str(args) + " kwargs is " + str(kwargs))
print('in wrapper, you add to do something')
return function(*args1, **kwargs1)
return func
return decorator
def test_first_wrapper():
@first_wrapper(1, 2, c=12)
def decoratored2(*args, **kwargs):
print('in fun, args is {0}, kwargs is {1}'.format(str(args), str(kwargs)))
decoratored2(2,3,4, b=5)
decoratored2(2, 3, 4, b=5)
def test_second_wrapper():
@first_double_wrapper(2,3) #此处实现了可以采用@first_double_wrapper的调用方式
def decoratored2(*args, **kwargs):
print('in fun, args is {0}, kwargs is {1}'.format(str(args), str(kwargs)))
decoratored2(2,3,5, b=7)
if __name__ == "__main__":
# test_wrapper()
# test_first_wrapper()
test_second_wrapper()
在上面的用另一个wraper来包装一个wrapper,使得@wrappe()这种格式可以用不要括号的第一种
wrapper格式来表示,则需要修改first_double_wrapper如上面的样子,然后使用另一个wrapper来
修改first_double_wrapper,其中主要的是将first_double_wrapper中的一个闭包函数移到double_wrapper
中,可以通过上面的first_double_wrapper与first_wrapper的代码可知道。
要想清楚的理解闭包,需要根据它的等价形式,然后通过闭包函数来构建相应的函数调用形式。
而对于本文提到的两个wrapper的改造形式,则要注意first_double_wrapper的抽象,以及将带参数的
wrapper的一个闭包移动到double_wrapper中,而使得将一个统一的处理过程放入到了first_double_wrapper中。
我认为在使用decorator的过程中,可以叠加的使用decorator,但是在构建decorator的过程中,可以从
最底层的靠近decorator的部分开始构造,然后在保证函数功能的基础上一步步的修改decorator函数。