在 对另一个问题的这个答案 的评论中,有人说他们不确定 functools.wraps
在做什么。所以,我问这个问题,以便在 StackOverflow 上有它的记录以供将来参考: functools.wraps
究竟是做什么的?
原文由 Eli Courtwright 发布,翻译遵循 CC BY-SA 4.0 许可协议
在 对另一个问题的这个答案 的评论中,有人说他们不确定 functools.wraps
在做什么。所以,我问这个问题,以便在 StackOverflow 上有它的记录以供将来参考: functools.wraps
究竟是做什么的?
原文由 Eli Courtwright 发布,翻译遵循 CC BY-SA 4.0 许可协议
从 python 3.5+ 开始:
@functools.wraps(f)
def g():
pass
是 g = functools.update_wrapper(g, f)
的别名。它只做了三件事:
__module__
, __name__
, __qualname__
, __doc__
, and __annotations__
attributes of f
在 g
上。此默认列表在 WRAPPER_ASSIGNMENTS
中,您可以在 functools 源 中看到它。__dict__
的 g
f.__dict__
。 (参见源代码中的 WRAPPER_UPDATES
)__wrapped__=f
属性 g
结果是 g
看起来与 f
具有相同的名称、文档字符串、模块名称和签名。唯一的问题是关于签名这实际上不是真的:它只是 inspect.signature
默认情况下遵循包装器链。您可以使用 inspect.signature(g, follow_wrapped=False)
进行检查,如 文档 中所述。这会产生恼人的后果:
Signature.bind()
的东西。现在在 functools.wraps
和装饰器之间存在一些混淆,因为开发装饰器的一个非常常见的用例是包装函数。但两者是完全独立的概念。如果您有兴趣了解差异,我为两者实现了帮助程序库: decopatch 可以轻松编写装饰器, makefun 可以为 @wraps
提供签名保留替代品。请注意, makefun
依赖于与著名的 decorator
库相同的经过验证的技巧。
原文由 smarie 发布,翻译遵循 CC BY-SA 4.0 许可协议
2 回答5.2k 阅读✓ 已解决
2 回答1.2k 阅读✓ 已解决
4 回答1.5k 阅读✓ 已解决
3 回答1.4k 阅读✓ 已解决
3 回答1.3k 阅读✓ 已解决
2 回答915 阅读✓ 已解决
1 回答1.8k 阅读✓ 已解决
当您使用装饰器时,您是在用另一个函数替换一个函数。换句话说,如果你有一个装饰器
那么当你说
这和说的完全一样
并且您的函数
f
被替换为函数with_logging
。不幸的是,这意味着如果你接着说它将打印
with_logging
因为那是你的新函数的名称。事实上,如果您查看f
的文档字符串,它将是空白的,因为with_logging
没有文档字符串,因此您编写的文档字符串将不再存在。此外,如果您查看该函数的 pydoc 结果,它不会被列为采用一个参数x
;相反,它将被列为*args
和**kwargs
因为这就是 with_logging 所需要的。如果使用装饰器总是意味着丢失有关函数的信息,那将是一个严重的问题。这就是为什么我们有
functools.wraps
。这需要一个装饰器中使用的函数,并添加复制函数名称、文档字符串、参数列表等的功能。并且由于wraps
本身是一个装饰器,以下代码做正确的事情: