【问题标题】:What impact does object mutability have on scope in python?对象可变性对 python 的作用域有什么影响?
【发布时间】:2012-04-08 02:23:14
【问题描述】:

以下(荒谬但说明性的)代码通过改变封闭函数中定义的列表按预期工作:

def outside1():
    l = list('abcd')
    def inside(a):
        print "Before - %i: %r" % (id(l), l)
        l.append(a)
        print "After - %i: %r\n" % (id(l), l)
    return inside

f = outside1()
[f(c) for c in 'efgh']

此代码还可以显示在封闭范围内定义的不可变对象可在封闭范围内访问:

def outside2():
    t = tuple('abcd')
    def inside():
        print "%i: %r" % (id(t), t)
    return inside

outside2()()

但是,local variable 't' referenced before assignment 失败:

def outside3():
    t = tuple('abcd')
    def inside(a):
        print "Before - %i: %r" % (id(t), t)
        t = t + (a,)
        print "After - %i: %r\n" % (id(t), t)
    return inside

f = outside3()
[f(c) for c in 'efgh']

有人可以解释这里发生了什么吗?我的第一个猜测是我可以改变但不能分配给封闭范围,但鉴于outside2 有效,我至少希望 before 打印语句有效。

【问题讨论】:

    标签: python scope closures


    【解决方案1】:

    马辛是对的。可变性对范围绝对没有影响。

    您需要了解的是,在第一个示例中,当您“改变l 指向的列表”时,您只是在读取变量l,然后对其调用一些方法(.append()) .这与在第二个示例中读取变量 t 的情况完全相同。

    在这两种情况下,您都没有分配给外部范围内的变量,只是读取它。可变性只是意味着您可以更改变量指向的内容,从而以这种方式共享更改。但从变量和作用域来看,绝对没有区别。

    在第三个示例中,您将分配给变量t。这就是区别。 Python 2.x 无法通过global 分配给除全局变量之外的外部变量。 Python 3.x 有nonlocal 允许你这样做。请注意,可变性与它无关:如果您在第一个示例中尝试分配(而不是仅仅改变指向的对象)变量l ,你会遇到同样的问题:

    def outside1():
        l = list('abcd')
        def inside(a):
            print "Before - %i: %r" % (id(l), l)
            l = [1,2,3]
            print "After - %i: %r\n" % (id(l), l)
        return inside
    

    【讨论】:

      【解决方案2】:

      Python 在编译时静态检测名称的范围:分配给函数内部的名称成为该函数的本地名称。所以这条线

      t = t + (a,)
      

      t 呈现为inside() 的局部变量,并且tinside() 中的任何查找都将尝试查找inside() 的局部变量。执行上述行时,t 尚不存在,因此报错。

      在 Python 3.x 中,您可以通过将 t 显式声明为 nonlocal 来解决该问题:

      def outside3():
          t = tuple('abcd')
          def inside(a):
              nonlocal t
              print("Before - %i: %r" % (id(t), t))
              t = t + (a,)
              print("After - %i: %r\n" % (id(t), t))
          return inside
      

      所有这些都与可变性完全无关。您使用列表的示例不会重新分配名称l,而使用元组的示例确实重新分配t;这是重要的区别,而不是可变性。

      【讨论】:

      • "Python 在编译时静态检测名称范围" 谢谢,这是简短的回答; py3k 提示是一个不错的奖励。
      【解决方案3】:

      可变性对范围没有影响。

      问题是分配给不在当前范围内的变量会创建该变量,而仅仅读取该变量不会。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-03
        • 2019-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-22
        相关资源
        最近更新 更多