Python动态装饰器 - 为什么有这么多wraps?

16 投票
2 回答
10651 浏览
提问于 2025-04-16 16:18

我对Python装饰器还是有点陌生——之前用过,但从来没自己写过。我在看这个教程(具体那一段),但我不太明白为什么我们需要三层函数?为什么不能直接这样做呢:

def decorator(func, *args, **kwargs):
    return func(*args,**kwargs)

谢谢 :)

2 个回答

4

装饰器是用来修改一个函数的,它会在这个函数外面加上一层包装。在你给函数加装饰的时候,这个函数还没有被调用,所以你还看不到任何参数(或者关键字参数)。现在你能做的就是创建一个新的函数,等到真正调用的时候,这个新函数会处理那些参数。

34

那么,如果你在一个函数上使用那个装饰器,会发生什么呢?

@decorator
def foo(): pass

这段代码会立即调用 foo,这不是我们想要的。装饰器会被调用,并且它的返回值会替换掉原来的函数。这就像是说

def foo(): pass
foo = decorator(foo)

所以,如果我们有一个会调用 foo 的装饰器,我们可能想要一个返回一个调用 foo 的函数的函数——这个返回的函数会替换掉 foo。

def decorator(f):
    def g(*args, **kwargs):
        return f(*args, **kwargs)
    return g

现在,如果我们想给装饰器传递一些选项,我们不能像你例子中那样把它们和函数并排传递。没有这样的语法。所以我们定义一个返回带参数的装饰器的函数。它返回的装饰器将是一个闭包。

def argument_decorator(will_I_call_f):
    def decorator(f):
        def g(*args, **kwargs):
            if will_I_call_f: return f(*args, **kwargs)
        return g
    return decorator

所以我们可以这样做

decorator = argument_decorator(True)
@decorator
def foo(): pass

而且 Python 提供了一种方便的语法,可以让你直接在函数调用中写:

@argument_decorator(True)
def foo(): pass

所有这些都是非装饰器语法的语法糖,等同于

def foo(): pass
foo = argument_decorator(True)(foo)

撰写回答