python - 为什么某些Python代码的包装器和包装函数是相同的。

标签 python wrapper python-decorators

我正在 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/

相关文章:

Python - CSV 文件,确保答案与询问的内容在同一行

python 长时间运行的守护进程作业处理器

dynamic - 在网格中指定本地动态

python - 为什么我们需要在装饰器中包装函数?

Python:我想装饰 MethodType 属性而不将属性类型更改为 FunctionType

python - 如何确保在子项在装饰器中覆盖它之前始终调用父方法?

python - 没有网络进程运行错误 - 在 Heroku 上部署 Django

python - 从外部模块调用 gimp-fu 函数

c# - 如何在 .NET 应用程序中使用 C++ 项目?

python - 让 Python 装饰器与 Hug API 框架配合使用