对于下面的函数,我想了解的是
i. 为什么是 wrapper.count = 0
在封装函数下面初始化?为什么不在def counter(func)下面初始化?而且为什么包装函数的counter不重设 wrapper.count
为0,因为它运行在封装函数下面?
我想知道什么是 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()')
装饰器出错了,在包装函数里面你需要。
return func(*args, **kwargs) # instead of `return func`
为什么
wrapper.count = 0
在包装函数下面初始化?
因为如果你在包装函数内部这样做,那么它总是会重设 wrapper.count
到 0
. 除非你检查它还没有被定义。(我在答案的最后给出了一个例子)。
为什么不在下面初始化
def counter(func)
?
因为那里没有定义封装函数。所以解释器会抱怨。
而为什么
wrapper.count
复位wrapper.count
到0
因为它是在封装函数下面执行的?
因为这个语句在你用 @counter
装饰器,并且它不会在每次调用 foo()
功能。
我想知道什么是
wrapper.count
?
这是一个函数属性。或多或少类似于 static
在C++等函数里面的变量。
为什么不直接初始化一个普通的变量
count
相对于wrapper.count
?
因为这将是一个局部变量,它将重设 count
在每次调用时变为0。
还有一种方法可以定义 wrapper.count = 0
内的包装函数。所以现在你不需要在包装函数之外定义它。wrapper
函数。
def counter(func):
def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'count'):
wrapper.count = 0
wrapper.count += 1
return func(*args, **kwargs)
return wrapper
在高层次上,装饰函数维护着一个被调用次数的计数器。
这段代码有一个主要问题。包装器实际上并没有像它应该的那样调用被包装的函数。而不是 return func
,它只是返回函数对象,它应该改为
return func(*args, **kwargs)
作为 @warvariuc指出其中一个可能的原因是,作者没有或不知道。nonlocal
,让你可以访问包围的命名空间。
我认为一个更合理的原因是,你希望能够访问计数器。函数是具有可变异字典的一级对象,你可以对它们进行任意的属性分配和访问。你可以对它们进行任意的属性分配和访问。可以方便地检查 foo.count
几次通话后,否则当初为什么要维护它?
原因是 wrapper.counter
的方式进行初始化,简单来说就是 wrapper
不存在于本地命名空间中,直到 def
语句运行来创建它。一个新的函数对象是由内部的 def
每逢 counter
. def
一般来说是每次运行时都会创建一个函数对象的赋值。
关于你所展示的代码,还有一个小问题,那就是 foo.__name__
将是 wrapper
而不是 foo
装饰后。为了减轻这种情况,使其更忠实地模仿原来的功能,你可以使用 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
我猜测这种模式最早出现在Python 2中,其中 nonlocal
语句不可用。在我看来,这段代码的作者只是试图像在C语言中一样模拟静态变量( https:/stackoverflow.coma279586248296。 ).
因为如果你试图使用一个在函数的顶层声明的普通变量 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()')