如何在装饰器中混合* args,** kwargs和命名参数

时间:2014-03-19 20:51:54

标签: python arguments decorator

我想创建一个名为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)设置{{ 1}},hook=1a=2。我该怎么做?

1 个答案:

答案 0 :(得分:1)

修改__defaults__是处理此问题的一种非常可疑的方法。 __defaults__用于参数默认值。如果您的钩子不会作为参数传递,则不应将它们存储为参数默认值。

如果你真的希望以后能够修改钩子,你应该让你的装饰器成为一个存储钩子的类。这是一个例子:

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