看一下这段代码:
def closure():
value = False
def method_1():
value = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
我希望它能打印'Value is:True',但事实并非如此。为什么这个和解决方案是什么?
答案 0 :(得分:16)
这是因为method_1
获得了自己的局部作用域,它可以声明变量。 Python看到value = True
并认为您正在创建一个名为value
的新变量,method_1
的本地变量。
Python这样做的原因是为了避免使用内部函数中的变量污染外部作用域的本地。 (您不希望在常规的模块级函数中进行赋值,从而导致创建全局变量!)
如果你没有分配给value
,那么Python搜索外部范围寻找变量(所以读取变量按预期工作,如{{1}所示})。
解决这个问题的一种方法是使用可变对象而不是分配:
method_2
在Python 3中,正好添加了nonlocal
statement(另请参阅docs):
result = { 'value': False }
def method_1():
result['value'] = True
答案 1 :(得分:2)
在method_1
中,Python假定(非常明智!)value
是一个局部变量。无论何时在函数内部分配变量名称,都假定该变量名称是新的局部变量。如果你希望它是全局的,那么你必须将它声明为global
,如果你希望它是“非本地的”,在Python 3中,你可以声明它nonlocal
,但是在Python 2中,你必须做一些更丑陋的事情:将值存储在容器中。这避免了必须重新分配变量名称,因此避免了范围模糊。
def method_1_global():
global value
value = True
def method_1_nonlocal_P3():
nonlocal value
value = True
value = [False]
def method_1_nonlocal_P2():
value[0] = True
答案 2 :(得分:2)
分配给变量时,它假定变量属于局部范围。因此value
中的method_1
不是value
中的closure
。
如果您希望在Python 3上使用此功能,请在method_1
添加一行:nonlocal value
。
在Python 2上,
def closure():
value = [False]
def method_1():
value[0] = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
是一种可能的解决办法。
答案 3 :(得分:2)
这是因为您实际上并未修改已关闭的变量 - 您正在使用具有相同名称的新变量对其进行屏蔽。在python 2.x中没有简单的方法,这就是在python 3中添加nonlocal
关键字的原因。
然而,这可以使用像list和dictionary这样的可变类型来解决。有good example in this answer。
答案 4 :(得分:1)
为避免这种情况,您可以使用列表。
value = [False]
def method_1():
value[0] = True
Python现在在更高级别的范围内搜索,因为 value 在局部变量中不可用。由于 value 是一个列表而Python将其引用为相对于* method_1 *的全局变量,因此您可以将 value 视为一个列表。