【问题标题】:Python and closed variablesPython 和封闭变量
【发布时间】:2011-10-13 12:02:33
【问题描述】:

看看这段代码:

def closure():
    value = False

    def method_1():
        value = True

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

    method_1()
    method_2()

closure()

我希望它打印“值是:真”,但事实并非如此。为什么会这样?解决方法是什么?

【问题讨论】:

  • 这个帖子很好地说明了为什么五分钟的免费编辑有时很糟糕。

标签: python closures python-2.x


【解决方案1】:

这是因为method_1 拥有自己的本地范围,它可以在其中声明变量。 Python 看到 value = True 并认为您正在创建一个名为 value 的新变量,它是 method_1 的本地变量。

Python 这样做的原因是为了避免内部函数的变量污染外部作用域的局部变量。 (您不希望在常规的模块级函数中进行赋值导致创建全局变量!)

如果您没有分配给value,那么 Python 会搜索外部范围以查找变量(因此读取变量按预期工作,正如您的 method_2 所证明的那样。

解决这个问题的一种方法是使用可变对象而不是赋值:

result = { 'value': False }

def method_1():
    result['value'] = True

在 Python 3 中,nonlocal statement(另见 docs)正是针对这种情况添加的:

def method_1():
    nonlocal value
    value = True    # Works as expected -- assigns to `value` from outer scope

【讨论】:

    【解决方案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
    

    【讨论】:

    • 请注意,global 不再是闭包的一部分,它可能会在程序的其他地方搞砸。
    • @agf -- 确实如此;我只是试图以尽可能一般的方式解释范围规则。
    【解决方案3】:

    当您分配给一个变量时,它假定该变量是局部范围的。所以method_1 中的value 不是closure 中的value

    如果您希望它在 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()
    

    是一种可能的解决方法。

    【讨论】:

      【解决方案4】:

      这是因为您实际上并没有修改封闭变量 - 您是用一个具有相同名称的新变量来掩盖它。在 python 2.x 中没有简单的方法可以解决这个问题,这就是为什么在 python 3 中添加了nonlocal 关键字。

      不过,这可以通过使用列表和字典等可变类型来解决。有一个good example in this answer

      【讨论】:

        【解决方案5】:

        为避免这种情况,您可以使用列表。

        value = [False]
        
        def method_1():
            value[0] = True
        

        Python 现在所做的是在更高级别的范围内搜索,因为 value 在局部变量中不可用。由于 value 是一个列表,而 Python 将其称为相对于 *method_1* 的全局变量,因此您可以将 value 视为一个列表。

        【讨论】:

          猜你喜欢
          • 2014-12-04
          • 2017-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多