繁体   English   中英

试图了解 Python 包装器

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM