我正在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
的目的。
答案 0 :(得分:1)
wraps
将来自其他函数的多个属性复制到此函数上 - 默认情况下,__module__
,__name__
,__qualname__
,__annotations__
和{{1} }}
最明显有用的是__doc__
。考虑这个更简单的例子: 1
__doc__
现在,如果有人想使用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
,他们将获得29行有用信息。 (或者,如果他们在PyCharm中自动完成help(mychild.spam)
,它会弹出叠加文档等等。)所有这些都不需要我手动复制和粘贴它。而且,更好的是,如果mychild.spam
来自我没有编写的某个框架,并且我的用户从该框架的1.2.3升级到1.2.4,并且有更好的文档字符串,他们会看到更好的文档字符串。
在最常见的情况下,Base
将是Child
的子类,而Base
将是覆盖。 2 但实际上并非如此required - spam
并不关心你是通过继承进行子类型化,还是仅通过实现隐式协议来打字;它对两种情况同样有用。只要wraps
旨在实现Child
中的spam
协议,Base
就可以拥有相同的文档字符串(以及其他元数据属性)。
其他属性可能不如docstrings有用。例如,如果您正在使用类型注释,那么它们在读取代码时的好处可能至少与它们能够运行Mypy进行静态类型检查一样高,因此只需从其他方法动态复制它们通常不是一切都很有用。 Child.spam
和__module__
主要用于反思/检查,在这种情况下更有可能产生误导而不是有用(尽管您可能想出一个您想要的框架示例人们要阅读__qualname__
中的代码而不是Base
中的代码,但对于默认的明显示例则不然。但是,除非它们是有害的,否则使用Child
而不仅仅是默认值的可读性成本可能不值得。
<子> 1。如果您使用的是Python 2,请将这些类更改为继承自@functools.wraps(Base.spam, assigned=('__doc__',))
;否则他们将成为旧式的课程,这会使事情变得无关紧要。如果是Python 3,那么就没有旧式的类,所以这个问题甚至都不会出现。
<子> 2。或者可能是ABC的“虚拟子类”,通过object
调用或通过子类钩子声明。
答案 1 :(得分:0)
@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'."