我正在 Github 上阅读 Ian Goodfellow 的 GAN 源代码(链接 https://github.com/goodfeli/adversarial/blob/master/deconv.py )。特别是,在第 40/41 行,代码是:
@functools.wraps(Model.get_lr_scalers)
def get_lr_scalers(self):
这是一种相当陌生的使用wraps
的方式,而且目标似乎是用用户定义的函数替换get_lr_scalers
。但在这种情况下,我们真的不需要包装器,对吗?在这种情况下,我真的不知道 wraps
的用途。
最佳答案
wraps
将另一个函数中的多个属性复制到此函数上 - 默认情况下,__module__
, __name__
, __qualname__
, __annotations__
和__doc__
.
最明显最有用的复制是 __doc__
。考虑这个更简单的示例:1
class Base:
def spam(self, breakfast):
"""spam(self, breakfast) -> breakfast with added spam
<29 lines of detailed information here>
"""
class Child:
@functools.wraps(Base.spam)
def spam(self, breakfast):
newbreakfast = breakfast.copy()
newbreakfast.meats['spam'] + 30
return newbreakfast
现在如果有人想使用help(mychild.spam)
,他们将获得 29 行有用信息。 (或者,如果它们在 PyCharm 中自动完成 mychild.spam
,它会弹出带有文档的覆盖层等),而我无需手动复制和粘贴它。而且,更好的是,如果 Base
来 self 没有编写的某个框架,我的用户从该框架的 1.2.3 升级到 1.2.4,并且有更好的文档字符串,他们会看到更好的文档字符串。
在最常见的情况下,Child
将是 Base
的子类,和spam
将是一个覆盖。2 但这实际上并不是必需的 — wraps
不关心你是通过继承进行子类型化,还是通过实现隐式协议(protocol)来进行鸭子类型化;它对于这两种情况同样有用。只要Child
旨在实现spam
协议(protocol)来自Base
,对于 Child.spam
是有意义的具有相同的文档字符串(可能还有其他元数据属性)。
其他属性可能不如文档字符串那么有用。例如,如果您使用类型注释,它们在读取代码方面的好处可能至少与它们能够运行 Mypy 进行静态类型检查的好处一样高,因此仅从另一个方法动态复制它们通常是不可行的所有这些有用的。和__module__
和__qualname__
主要用于反射/检查,在这种情况下更有可能产生误导而不是有帮助(尽管您可能会想出一个框架示例,您希望人们阅读 Base
中的代码,而不是Child
中的代码,这对于默认的明显示例来说并非如此)。但是,除非它们非常有害,否则使用 @functools.wraps(Base.spam, assigned=('__doc__',))
的可读性成本而不仅仅是默认值可能不值得。
<子>1。如果您使用的是 Python 2,请将这些类更改为继承自 object
;否则它们将是旧式类,这只会以不相关的方式使事情变得复杂。如果Python 3,没有旧式类,所以这个问题根本不会出现。
<子>2。或者也许是 ABC 的“虚拟子类”,通过 register
声明调用,或通过子类 Hook 。
关于python - 为什么某些Python代码的包装器和包装函数是相同的。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50162847/