我不认为其他答案叙述了整个故事,所以这是我的 2 美分。
不同类型的范围没有区别。这不是这里发生的事情。
重新绑定名称,无论该名称指的是什么,都会导致发生未绑定的本地错误。
Python 中的某些类型是可变的(一旦创建,值就可以更改),list 就是其中之一。有些是不可变的,更改这些类型的值需要创建一个新对象。
所以,对于第一个例子,
def foo():
a = 0
def bar():
a += 1
bar()
foo()
这不起作用,因为您实际上有一个任务在这里进行,a = a + 1。您可以使用non-local 来完成上述工作,这不是重点。
对列表做同样的事情:
def foo():
a = []
def bar():
a.append(1)
bar()
foo()
这确实有效。这里没有任务。
您不能将名称重新绑定到不同的对象,但如果对象是可变的,您可以修改其内容。
现在,还有 2 种情况您应该注意。
def foo():
a = []
c = []
def bar():
a = c + [1]
bar()
print(a)
print(c)
foo()
这会起作用,但是您应该注意,bar() 内的 a 现在是 bar 的本地,并且 print() 语句应该反映这一点。
但这里有个问题
def foo():
a = []
def bar():
a = a + [1] #or even a += [1], doesn't matter
bar()
print(a)
foo()
这行不通! (将这个 sn-p 与第一个 sn-p 进行对比很重要,因为这说明了为什么这与作用域无关。花一分钟再读一遍。)
所以这是行不通的,理解它很重要。
如果在函数内部对变量进行赋值,则该变量被视为局部变量。
现在,在最后一种情况下,当我们执行 a = c + 1 时,它更像是 a_local = c_nonlocal + 1。
在这种情况下,a = a + 1 是 a_local = a_local + 1,因此,这确实会导致错误。
这就是为什么,重新绑定一个名字,不管这个名字指的是什么,总是会导致一个未绑定的本地错误发生。
在前面的情况下(第三个sn -p),它没有重新绑定它——它正在创建一个局部变量。
在后一种情况下(第四个 sn-p),它实际上是重新绑定,因此出现错误。