试图理解 Python 包装器
Trying to understand a Python wrapper
对于下面的函数,我正在尝试理解
我。为什么 wrapper.count = 0
在包装函数下面初始化?为什么不在 def counter(func) 下面初始化?为什么 wrapper.count 不将 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()')
Why not just initialise a normal variable count as opposed to variable.count
我的猜测是这种模式首先出现在 Python 2 中,其中 nonlocal
语句不可用。在我看来,该片段的作者只是试图模拟 C 语言中的静态变量()。
因为如果您尝试使用在函数 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()')
装饰器出错,您需要在包装器函数内部:
return func(*args, **kwargs) # instead of `return func`
Why is wrapper.count = 0
initialised below the wrapper function?
因为如果您在包装函数中执行此操作,那么它将始终将 wrapper.count
的值重置为 0
。除非您检查它是否尚未定义。 (我在回答的最后给出了一个例子。)
Why not initialised below def counter(func)
?
因为那里没有定义包装函数。所以口译员会抱怨的。
And why doesn't the wrapper.count
reset the wrapper.count
to 0
since it is executed below the wrapper function?
因为当你用@counter
装饰器包装一个函数时,这条语句只执行一次,而不会在你每次调用foo()
函数时都执行。
And I'm trying to understand what is wrapper.count
?
这是一个函数属性。或多或少类似于 C++ 等函数中的 static
变量
Why not just initialise a normal variable count
as opposed to 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
,它只是 returns 函数对象,它应该是
return func(*args, **kwargs)
作为 ,一个可能的原因是作者没有或不知道 nonlocal
,它允许您访问封闭的命名空间。
我认为一个更合理的原因是您希望能够访问柜台。函数是具有可变字典的 first-class 对象。您可以分配和访问它们的任意属性。调用几次后检查foo.count
可能很方便,否则为什么要首先维护它?
wrapper.counter
被初始化的原因很简单, wrapper
在 def
语句 运行 之前不存在于本地命名空间中它。每次 运行 counter
时,内部 def
都会创建一个新的函数对象。 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
对于下面的函数,我正在尝试理解
我。为什么 wrapper.count = 0
在包装函数下面初始化?为什么不在 def counter(func) 下面初始化?为什么 wrapper.count 不将 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()')
Why not just initialise a normal variable count as opposed to
variable.count
我的猜测是这种模式首先出现在 Python 2 中,其中 nonlocal
语句不可用。在我看来,该片段的作者只是试图模拟 C 语言中的静态变量()。
因为如果您尝试使用在函数 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()')
装饰器出错,您需要在包装器函数内部:
return func(*args, **kwargs) # instead of `return func`
Why is
wrapper.count = 0
initialised below the wrapper function?
因为如果您在包装函数中执行此操作,那么它将始终将 wrapper.count
的值重置为 0
。除非您检查它是否尚未定义。 (我在回答的最后给出了一个例子。)
Why not initialised below
def counter(func)
?
因为那里没有定义包装函数。所以口译员会抱怨的。
And why doesn't the
wrapper.count
reset thewrapper.count
to0
since it is executed below the wrapper function?
因为当你用@counter
装饰器包装一个函数时,这条语句只执行一次,而不会在你每次调用foo()
函数时都执行。
And I'm trying to understand what is
wrapper.count
?
这是一个函数属性。或多或少类似于 C++ 等函数中的 static
变量
Why not just initialise a normal variable
count
as opposed towrapper.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
,它只是 returns 函数对象,它应该是
return func(*args, **kwargs)
作为 nonlocal
,它允许您访问封闭的命名空间。
我认为一个更合理的原因是您希望能够访问柜台。函数是具有可变字典的 first-class 对象。您可以分配和访问它们的任意属性。调用几次后检查foo.count
可能很方便,否则为什么要首先维护它?
wrapper.counter
被初始化的原因很简单, wrapper
在 def
语句 运行 之前不存在于本地命名空间中它。每次 运行 counter
时,内部 def
都会创建一个新的函数对象。 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