如何在装饰器中混合 *args, **kwargs 和命名参数
我想创建一个自定义的装饰器,叫做 captain_hook
。这个装饰器应该能存储一些 hooks
,也就是在被装饰的函数执行之前要调用的函数。
def captain_hook(func):
def closure(hooks=[], *args, **kwargs):
for hook in hooks:
hook(*args, **kwargs)
return func(*args, **kwargs)
return closure
现在我可以这样说:
@captain_hook
def sum(a, b):
return a+b
sum.__defaults__[0].append(lambda arg: sys.stdout.write(str(arg))) #this lambda function prints argument
sum(1, 2) #should write (1, 2) and return 3
问题是我无法把参数传递给 closure
,因为 args
的第一个元素错误地被当作 hooks
的参数了:比如 sum(1, 2)
会把 hook=1
,a=2
,b
变成未定义。那我该怎么做呢?
1 个回答
2
修改 __defaults__
这种做法其实不太靠谱。 __defaults__
是用来存放参数默认值的。如果你的钩子(hooks)不会作为参数传递,那就不应该把它们当作参数的默认值来存储。
如果你真的想在之后能够修改这些钩子,建议你把装饰器做成一个类,这样可以把钩子存储在这个类里面。下面是一个例子:
class CaptainHook(object):
def __init__(self, func):
self.hooks = []
self.func = func
def __call__(self, *args, **kwargs):
for hook in self.hooks:
hook(*args, **kwargs)
return self.func(*args, **kwargs)
然后:
>>> @CaptainHook
... def sum(a, b):
... return a+b
>>> sum(1, 2)
3
>>> def aHook(*args, **kwargs):
... print "I am a hook!"
>>> sum.hooks.append(aHook)
>>> sum(1, 2)
I am a hook!
3