对象可变性对python中的作用域有什么影响?

时间:2012-03-23 14:11:04

标签: python scope closures

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

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有效。

3 个答案:

答案 0 :(得分:6)

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;这是重要的区别,而不是可变性。

答案 1 :(得分:3)

可变性对范围没有影响。

问题是对不在当前范围内的变量的赋值会创建该变量,而仅仅读取该变量则不会。

答案 2 :(得分:1)

Marcin是对的。可变性对范围完全没有影响。

您需要了解的是,在第一个示例中,当您正在变更列表时,#34;由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