在解析源码中的装饰器时,Python 会把被装饰的函数作为第一个参数传给装饰器函数,那怎么让装饰器接受其他参数呢?
答案是建立一个装饰器工厂函数,返回一个装饰器,然后再把它应用到要装饰的函数上。
举例:
# BEGIN REGISTRATION
registry = [] # <1>
def register(func): # <2>
print('running register(%s)' % func) # <3>
registry.append(func) # <4>
return func # <5>
@register # <6>
def f1():
print('running f1()')
@register
def f2():
print('running f2()')
def f3(): # <7>
print('running f3()')
def main(): # <8>
print('running main()')
print('registry ->', registry)
f1()
f2()
f3()
if __name__=='__main__':
main() # <9>
# END REGISTRATION
为了便于 register 执行的函数注册供能,我们提供一个可选的 active 参数,设为 False 时,不注册被装饰的函数。
# BEGIN REGISTRATION_PARAM
registry = set() # <1>
def register(active=True): # <2>
def decorate(func): # <3>
print('running register(active=%s)->decorate(%s)'
% (active, func))
if active: # <4>
registry.add(func)
else:
registry.discard(func) # <5>
return func # <6>
return decorate # <7>
@register(active=False) # <8>
def f1():
print('running f1()')
@register() # <9>
def f2():
print('running f2()')
def f3():
print('running f3()')
# END REGISTRATION_PARAM
如上所示,新的 register 函数不是装饰器,而是装饰器工厂函数,调用它会返回真正的装饰器,这才是应用到目标函数的装饰器。
只有 f2 函数在 register 中,f1 不在其中,因为传给 register 装饰器工厂函数的参数时 active = False,所以应用到 f1 上的 decorate 没有把它添加到 registry 中。decorate 是装饰器,register 是装饰器工厂函数。