带有关闭的python计数器

Vin*_*ang 2 python closures

我正在尝试使用闭包的属性在python中建立一个计数器。以下代码中的代码有效:

def generate_counter():
    CNT = [0]
    def add_one():
        CNT[0] = CNT[0] + 1
        return CNT[0]
    return add_one
Run Code Online (Sandbox Code Playgroud)

但是,当我将列表CNT更改为var时,它不起作用:

def generate_counter1():
    x = 0
    def add_one():
        x = x + 1
        return x
    return add_one
Run Code Online (Sandbox Code Playgroud)

当我打印实例的闭包属性时,我发现__closure__第二种情况是没有:

>>> ct1 = generate_counter()
>>> ct2 = generate_counter1()
>>> print(ct1.__closure__[0])
<cell at 0xb723765c: list object at 0xb724370c>
>>> print(ct2.__closure__)
None
Run Code Online (Sandbox Code Playgroud)

只是想知道为什么外部函数的索引必须是列表?


感谢您的回答!找到可以清楚说明此问题的文档 https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

Mar*_*ers 7

Python通过查看名称绑定行为来确定名称的范围;分配就是这样一种行为(功能参数,导入,目标for target ...while .. as target其他示例)。您在函数中绑定的名称被认为是本地名称。请参阅参考文档的“ 命名和绑定”部分

因此,x第二个示例中的名称是局部变量,因为您直接为其分配了变量:

x = x + 1
Run Code Online (Sandbox Code Playgroud)

实际上,由于从未提供x过本地值,因此在尝试使用该函数时会出现异常。当您尝试阅读时,本地名称是未绑定的

>>> generate_counter1()()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in add_one
UnboundLocalError: local variable 'x' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

在您的第一个示例中,没有这种绑定发生;你是不是改变内容CNT,那是什么名字引用不会改变。

如果您使用的是Python 3,则可以使用以下nonlocal语句覆盖将名称设为本地名称的决定:

def generate_counter2():
    x = 0
    def add_one():
        nonlocal x
        x = x + 1
        return x
    return add_one
Run Code Online (Sandbox Code Playgroud)

通过设置x非本地,Python在父上下文中找到它,并再次为其创建一个闭包。

>>> def generate_counter2():
...     x = 0
...     def add_one():
...         nonlocal x
...         x = x + 1
...         return x
...     return add_one
...
>>> generate_counter2().__closure__
(<cell at 0x1078c62e8: int object at 0x1072c8070>,)
Run Code Online (Sandbox Code Playgroud)

nonlocal是Python 3中的新功能;在Python 2中,您只能使用一些技巧,例如使用可变列表对象来规避绑定规则。另一个技巧是将计数器分配给嵌套函数的属性。同样,这避免了在当前范围内绑定名称:

def generate_counter3():
    def add_one():
        add_one.x += 1
        return add_one.x
    add_one.x = 0
    return add_one
Run Code Online (Sandbox Code Playgroud)