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

0 投票
1 回答
590 浏览
提问于 2025-04-17 23:01

我想创建一个自定义的装饰器,叫做 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=1a=2b 变成未定义。那我该怎么做呢?

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

撰写回答