以下(荒谬但说明性的)代码通过改变封闭函数中定义的列表按预期工作:
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
有效。
答案 0 :(得分:6)
Python在编译时静态检测名称的范围:分配给函数内部的名称变为该函数的本地名称。这一行
t = t + (a,)
将t
本地呈现为inside()
,t
内inside()
的任何查找都会尝试查找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