我正在尝试使用closure属性在python中构建一个计数器。以下代码中的代码:
def generate_counter():
CNT = [0]
def add_one():
CNT[0] = CNT[0] + 1
return CNT[0]
return add_one
但是,当我将列表CNT更改为var时,它不起作用:
def generate_counter1():
x = 0
def add_one():
x = x + 1
return x
return add_one
当我打印实例的closure属性时,我发现第二种情况的__closure__
是none:
>>> ct1 = generate_counter()
>>> ct2 = generate_counter1()
>>> print(ct1.__closure__[0])
<cell at 0xb723765c: list object at 0xb724370c>
>>> print(ct2.__closure__)
None
只是想知道为什么外部函数中的索引必须是一个列表?
感谢您的回答!找到了明确解释这个问题的文档 https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
答案 0 :(得分:4)
Python通过查看名称绑定行为来确定名称的范围;赋值是一种这样的行为(函数参数,导入,for target ...
或while .. as target
中的目标是其他示例)。您在函数中绑定的名称被视为 local 。请参阅参考文档的Naming and Binding section。
因此,第二个示例中的名称x
是 local 变量,因为您直接指定了该变量:
x = x + 1
事实上,因为您从未向x
提供本地值,所以当您尝试使用该函数时,您将获得异常;当您尝试阅读本地名称 unbound 时:
>>> 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
在你的第一个例子中没有发生这种绑定;您正在改变CNT
的内容,该名称引用的内容不会被更改。
如果您使用的是Python 3,则可以使用nonlocal
statement覆盖使名称成为本地名称的决定:
def generate_counter2():
x = 0
def add_one():
nonlocal x
x = x + 1
return x
return add_one
通过使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>,)
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
答案 1 :(得分:0)
它没有 成为一个列表,它只需要是一个可变对象,你 mutate ,而不是重新分配。
来自docs:
如果在函数体内的任何位置为变量赋值,则除非明确声明为全局,否则将其视为局部值。
因此,在您的第二个示例中,x
被视为本地(对于内部函数),并且在您第一次分配后,您将对外部&#39; <阴影进行遮蔽。< / p>
另一方面,在第一个示例中(因为您没有为CNT
分配值),它在外部函数中定义的CNT
上运行。