[英]Trying to understand a Python wrapper
对于下面的 function,我试图理解
一世。 为什么wrapper.count = 0
在包装器 function 下方初始化? 为什么不在 def counter(func) 下面初始化? 为什么 wrapper.count 不将wrapper.count
重置为 0,因为它运行在包装器 function 之下?
我试图了解什么是wrapper.count
? 为什么不初始化一个普通变量count
而不是wrapper.count
?
def counter(func):
def wrapper(*args, **kwargs):
wrapper.count += 1
# Call the function being decorated and return the result
return func
wrapper.count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
装饰器出现错误,在包装器 function 内,您需要:
return func(*args, **kwargs) # instead of `return func`
为什么
wrapper.count = 0
在包装器 function 下方初始化?
因为如果您在 wrapper function 内部执行此操作,那么它将始终将wrapper.count
的值重置为0
。 除非您检查它是否尚未定义。 (我在答案的最后给出了一个例子。)
为什么不在
def counter(func)
下面初始化?
因为包装器 function 没有在那里定义。 所以口译员会抱怨它。
为什么
wrapper.count
不将wrapper.count
重置为0
,因为它是在包装器 function 之下执行的?
因为当你用@counter
装饰器包装一个 function 时,这个语句只执行一次,而且它不会在你每次调用foo()
function 时执行。
我试图了解什么是
wrapper.count
?
这是一个 function 属性。 或多或少类似于 C++ 等函数内部的static
变量。
为什么不初始化一个普通变量
count
而不是wrapper.count
?
因为那将是一个局部变量,它会在每次调用时将count
重置为 0。
还有另一种方法可以在包装器 function 中定义wrapper.count = 0
。 所以现在你不需要在wrapper
function 之外定义它。
def counter(func):
def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'count'):
wrapper.count = 0
wrapper.count += 1
return func(*args, **kwargs)
return wrapper
在高层次上,装饰的 function 维护一个计数器,记录它被调用的次数。
代码存在一个主要问题。 包装器实际上并未按应有的方式调用包装的 function。 而不是return func
,它只返回 function object,它应该是
return func(*args, **kwargs)
正如@warvariuc 指出的那样,一个可能的原因是作者没有或不知道nonlocal
,它可以让您访问封闭的命名空间。
我认为一个更合理的原因是您希望能够访问柜台。 函数是具有可变字典的一流对象。 您可以分配和访问它们的任意属性。 几次调用后检查foo.count
可能会很方便,否则为什么要首先维护它?
wrapper.counter
以这种方式初始化的原因仅仅是在def
语句运行以创建它之前,本地命名空间中不存在wrapper
。 每次运行counter
时,内部def
都会生成一个新的 function object 。 def
通常是每次运行时创建 function object 的分配。
关于您显示的代码的另一个小问题是foo.__name__
在装饰之后将是wrapper
而不是foo
。 为了缓解这种情况,并使其更忠实地模仿原始 function,您可以使用functools.wraps
,它是装饰器包装器的装饰器。 您的代码如下所示:
from functools import wraps
def counter(func):
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.count += 1
# Call the function being decorated and return the result
return func(*args, **kwargs)
wrapper.count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
现在你可以做
>>> foo.__name__
'foo'
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo()
calling foo()
>>> foo.count
3
为什么不初始化一个正常的变量计数而不是
variable.count
我的猜测是这种模式首先出现在nonlocal
2 中,其中 nonlocal 语句不可用。 在我看来,该片段的作者只是试图模拟 static 变量,例如 C 语言( https://stackoverflow.com/296/7 )
因为如果您尝试使用在 function counter
的顶层声明的普通变量,您将无法在wrapper
中分配给它。
如果您将count
放在counter
下方,您将使其成为全局变量,因此它将在装饰器的所有实例之间共享,这可能不是所需的行为:
count = 0
def counter(func):
def wrapper(*args, **kwargs):
global count
count += 1
return func(*args, **kwargs)
return wrapper
@counter
def foo():
print('calling foo()')
这是一个nonlocal
版本(Python 3+):
def counter(func):
def wrapper(*args, **kwargs):
nonlocal count
count += 1
# Call the function being decorated and return the result
return func(*args, **kwargs)
count = 0
# Return the new decorated function
return wrapper
# Decorate foo() with the counter() decorator
@counter
def foo():
print('calling foo()')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.