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

2024-06-16 13:42:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在Github(linkhttps://github.com/goodfeli/adversarial/blob/master/deconv.py)中阅读Ian Goodfellow的GAN源代码。特别是在第40/41行,代码为:

@functools.wraps(Model.get_lr_scalers)
def get_lr_scalers(self):

这是一种相当陌生的使用wraps的方法,似乎目标是用用户定义的函数替换get_lr_scalers。但是那样的话,我们就不需要包装了,对吧?我真的不知道在这种情况下wraps的目的。你知道吗


Tags: pygithubmastercomgetblobadversariallr
2条回答

^{}默认情况下,将另一个函数的许多属性复制到此函数上,__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来自某个我没有编写的框架,并且我的用户从该框架的1.2.3升级到1.2.4,并且有一个更好的docstring,他们会看到更好的docstring。你知道吗


在最常见的情况下,Child将是Base的一个子类,spam将是一个重写。2但这实际上不是必需的-wraps并不关心您是通过继承进行子类型化,还是通过实现隐式协议进行duck类型化;这两种情况都同样有用。只要Child打算实现来自Basespam协议,那么Child.spam具有相同的docstring(可能还有其他元数据属性)是有意义的。你知道吗


其他属性可能没有docstring那么有用。例如,如果您使用的是类型注释,那么它们在读取代码方面的优势可能至少与能够运行Mypy进行静态类型检查方面的优势一样高,因此仅从另一个方法动态复制它们通常并不那么有用。和__module____qualname__主要用于反射/检查,在这种情况下更可能是误导而不是帮助(尽管您可能会提出一个框架示例,希望人们阅读Base中的代码,而不是Child中的代码,但对于默认的明显示例,这不是正确的)。但是,除非它们是有害的,否则使用@functools.wraps(Base.spam, assigned=('__doc__',))而不是默认值的可读性成本可能不值得。你知道吗


1。如果您使用的是python2,请将这些类更改为从object继承;否则它们将是旧式的类,这只会以一种不相关的方式使事情复杂化。如果是Python3,则没有旧式类,因此甚至不会出现此问题。

2。或者可能是ABC的“虚拟子类”,通过register调用或子类挂钩声明。

@wraps的目的是将一个函数的元信息复制到另一个函数。这通常是在通过包装替换原始函数时完成的,这通常是由装饰器完成的。你知道吗

但在一般情况下,下面是一个例子:

def f1():
    """Function named f1. Prints 'f1'."""
    print('f1')

@functools.wraps(f1)
def f2():
    print('f2')

现在,您可以测试发生了什么:

>>> f1
<function f1 at 0x006AD8E8>
>>> f2
<function f1 at 0x006AD978>
>>> f1()
f1
>>> f2()
f2
>>> f1.__doc__
"Function named f1. Prints 'f1'."
>>> f2.__doc__
"Function named f1. Prints 'f1'."

当您调用f2时,很明显它实际上是f2,但是当您检查它时,它的行为就像f1——它有相同的文档字符串和相同的名称。你知道吗

那有什么用?为此:

f1 = f2

现在,原来的f1被一个新功能所取代,但从外观上看它仍然像f1。你知道吗

通常是在装修工那里完成的:

def replace(func):
    @functools.wraps(func)
    def replacement():
        print('replacement')
    return replacement

@replace
def f1():
    """Function named f1. Prints 'f1'."""
    print('f1')

它的行为是这样的:

>>> f1()
replacement
>>> f1
<function f1 at 0x006AD930>
>>> f1.__name__
'f1'
>>> f1.__doc__
"Function named f1. Prints 'f1'."

相关问题 更多 >