Python和封闭变量

时间:2011-07-26 14:50:19

标签: python closures python-2.x

看一下这段代码:

def closure():
    value = False

    def method_1():
        value = True

    def method_2():
        print 'value is:', value

    method_1()
    method_2()

closure()

我希望它能打印'Value is:True',但事实并非如此。为什么这个和解决方案是什么?

5 个答案:

答案 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 视为一个列表。