我正在尝试使用闭包的属性在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
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)
| 归档时间: |
|
| 查看次数: |
1446 次 |
| 最近记录: |