一般咱们见到的简单装饰器这样的:编程
import json import functools def json_output(func): @functools.wraps(decorated) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result) return inner @json_output def f(): return {'status': 'done'}
当装饰器应用于函数 f
上时,它接受 f
做为其参数,返回一个函数 inner
,且将他绑定到变量f上。json
示例中咱们编写的装饰器 json_output
只接受一个隐式参数——即被装饰的方法,在使用此装饰器时自己看上去是并无参数的。然而有时候须要让装饰器自身带有一些须要的信息,从而使装饰器可使用恰当的方式装饰方法。好比上面的例子中,咱们想经过向装饰器传入不一样的参数来控制输出结果的缩进(indent)和排序(sort)。咱们能够这么作:函数
import json import functools def json_output(indent=None, sort_keys=False): def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner return actual_decorator @json_output(indent=4) def f(): return {'status': 'done'}
初次看起来会以为比较绕人,由于函数里嵌套了两个函数定义,然而实际上和以前一个版本的区别在于为了接收json序列化的参数多包装了一层,因此code
@json_output(indent=4) def f(): return {'status': 'done'} # 至关于 @actual_decorator def f(): return {'status': 'done'}
这样看起来就会明晰不少。排序
实际上, 装饰器里的 @
后接收一个函数,该函数以被装饰的函数(例子中是f)为参数,而且返回一个函数。当须要在装饰函数的同时传入参数的话,那么就须要多包装一层,先传入参数(例子中是 indent=4
)返回一个装饰的函数(例子中是 actual_decorator
), 这个返回的的函数 就跟之前同样接受被装饰的函数(f)做为参数而且返回一个函数做为装饰最后的方法供调用。import
然而当咱们像上面那样定义装饰器时,就不能这样调用:变量
import json import functools def json_output(indent=None, sort_keys=False): def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner return actual_decorator @json_output def f(): return {'status': 'done'}
在实际的项目过程当中,有时会出现这样的情况: 一开始写的装饰器时不须要使用时传参数的,后来发现有必要传参数,改好后原来不传参的装饰器不能正常使用了,这是修改原来使用的地方是项痛苦的事情。
这时候就须要对装饰器作一个兼容,使它在如下状况均可用:序列化
@json_output @json_output() @json_output(indent=4)
具体作法以下:方法
import json import functools def json_output(decorated_=None, indent=None, sort_keys=False): if decorated_ and (indent or sort_keys): raise def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner if decorated_: return actual_decorator(decorated_) else: return actual_decorator @json_output(indent=4) def f1(): return {'status': 'done'} @json_output def f2(): return {'status': 'done'} @json_output() def f3(): return {'status': 'done'} print f1() print f2() print f3()
代码中关键的地方在于 json_output
在最后对参数 decorated
进行了判断,有的话证实是不传参调用,那么直接返回 actual_decorator
的调用;没有的话则表明是传参类型的调用(虽然参数可能不存在),那么返回 actual_decorator
。其中有点须要注意, josn_output
的传参须要使用关键字参数,若是像下面这样直接传一个位置参数,那么根据如今的实现会出现错误(由于它会被当成 decorated_
)。im
@json_output(4) #错误的使用方法 def f4(): return {'status': 'done'}
《Python高级编程》 by Luke Sneeriger