装饰器模块与functools.wraps有何区别?

12
decorator模块functools.wraps的功能密切相关。那么这两者的区别是什么(基于Python 3.3 / 3.4)?
我知道其中一个区别:在三年前,decorator支持帮助文档,而wraps不支持(参见这里),请注意也有这个链接。

另一个例子:保留装饰函数的签名 - jfs
3个回答

6

和BrenBarn讨论后,现在functools.wraps也会保留被包装函数的签名。在我看来,这使得decorator装饰器几乎已经过时了。

from inspect import signature
from functools import wraps

def dec(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

def dec2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

def foo(a: int, b):
    pass

print(signature(dec(foo)))
print(signature(dec2(foo)))

# Prints:
# (*args, **kwargs)
# (a:int, b)

请注意,必须使用 signature,而不是 getargspec。已在 Python 3.4 上进行了测试。

getargspec 或者更新且推荐的 getfullargspec 单独使用时无法正常工作,但是当与 inspect.unwrap 结合使用时可以正常工作:https://docs.python.org/3/library/inspect.html#inspect.unwrap - Pekka Klärck

6

其中一个主要的区别在你提供的文档中已经列出:decorator 保留了被包装函数的签名,而 wraps 则没有。


1
这似乎不再是事实,是吗? - chtenb
@ChieltenBrinke: 你的意思是functools.wraps现在保留签名吗?我在文档中没有看到任何相关内容,但是我没有安装Python 3.5来测试它。 - BrenBarn
虽然文档中没有提到,但在 Python shell(3.4)中测试似乎表明它确实有效。 - chtenb
@ChieltenBrinke:对我不起作用。你测试了什么?你读了问题中链接的“装饰器”文档吗?通过“保留签名”,我们指的是inspect.getargspec返回原始函数的“真实”参数,而不是通用的*args/**kwargs签名。 - BrenBarn
@ChieltenBrinke:啊哈,你说得对。getargspec现在已经被弃用了,而signature运行正常。 - BrenBarn
根据Python 3.7的规定:functools.wraps保留了函数签名,但只是表面上的,因为helpsignature遵循__wrapped__属性;在误用的情况下,它不会引发正确的错误。请参见下面的答案:https://dev59.com/uGYr5IYBdhLWcg3wKHEp#55363013 - smarie

5
有两个区别:
- `decorator` 真正地保留了签名,而 `functools.wraps` 并没有,在 Python 3.7 中也是如此。所谓签名,是指 `help()`、`signature()` 和所有的 `__dict__`,当然还有包装器在用户提供错误参数时可以正确抛出 `TypeError` 而不执行任何操作。正如这篇文章中所解释的那样,`functools.wraps` 看起来保留了签名,但实际上并没有真正地保留它。 - 当参数不是可变位置时,使用 `decorator` 可以始终在 `kwargs` 中接收参数,这使得实现包装器变得更加容易。而使用 `functools.wraps` 可能更难根据名称获取参数值,因为它可能在 `*args`、`**kwargs` 或者没有(如果它是一个可选参数且用户没有提供)。
既然我非常喜欢 `functools.wraps` 的 API,但又想解决以上两个问题,所以我创建了 makefun。它提出了一种对 `@wraps` 进行了推广的方法,使用与 `decorator` 完全相同的技巧,甚至支持修改签名,例如添加和删除参数。它已经被多个项目使用,请不要犹豫尝试!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接