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

Fin*_*inn 1 python closures scope

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

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']
Run Code Online (Sandbox Code Playgroud)

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

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

outside2()()
Run Code Online (Sandbox Code Playgroud)

然而,这失败了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']
Run Code Online (Sandbox Code Playgroud)

有人能解释一下这里发生了什么吗?我的第一个猜测是我可以变异而不是分配到封闭范围,但我至少会预期打印前的声明可以工作outside2.

Sve*_*ach 6

Python在编译时静态检测名称的范围:分配给函数内部的名称变为该函数的本地名称.所以行

t = t + (a,)
Run Code Online (Sandbox Code Playgroud)

渲染t本地inside(),并且t内部的任何查找inside()都会尝试查找局部变量inside().当上述行被规定时,t尚不存在,因此出现错误.

在Python 3.x中,您可以通过明确声明tnonlocal:

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
Run Code Online (Sandbox Code Playgroud)

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