我想知道为什么这不能按预期工作的细节:
def outer():
mylist = []
def inner():
mylist += [1]
inner()
outer()
特别是因为mylist.__iadd__([1])
工作正常。
答案 0 :(得分:10)
问题在于,当您在函数内部分配变量名时,Python会假设您正在尝试创建一个新的局部变量,该变量将掩盖外部作用域中类似命名的变量。由于+=
必须获取 mylist
的值才能修改它,因此会抱怨,因为尚未定义mylist
的本地版本。 MRAB's answer给出了语义的清晰解释。
另一方面,当您执行mylist.__iadd__([1])
时,您不会在函数内部分配新的变量名称。您只是使用内置方法来修改已分配的变量名称。只要您不尝试为mylist
分配新值,就不会有问题。出于同样的原因,如果mylist[0] = 5
中inner
的定义为mylist
,则行outer
也可以在mylist = [1]
内使用。
但请注意,如果您尝试在函数的任何位置为mylist
分配新值,mylist.__iadd__([1])
确实会失败:
>>> outer()
>>> def outer():
... mylist = []
... def inner():
... mylist.__iadd__([1])
... mylist = []
... inner()
...
>>> outer()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in outer
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'mylist' referenced before assignment
如果要为包含范围的变量分配新值,则在3.0+中可以使用nonlocal
,就像使用global
分配新值一样全局范围内的变量。所以不要这样:
>>> mylist = []
>>> def inner():
... global mylist
... mylist += [1]
...
>>> inner()
>>> mylist
[1]
你这样做:
def outer():
mylist = []
def inner():
nonlocal mylist
mylist += [1]
inner()
print(mylist)
outer()
答案 1 :(得分:5)
如果在函数中绑定了一个名称(已赋值变量),则该名称将被视为本地名称,除非它被声明为全局。
因此,在inner
中,mylist
是本地的。
当您编写x += y
时,Python会在运行时尝试:
x = x.__iadd__(y)
如果失败,Python会尝试:
x = x.__add__(y)
答案 2 :(得分:1)
分配给的任何变量都假定为局部范围。
要分配给全局变量:
global var
var = 5
你无法完成你在Python 2中所做的事情,但在Python 3中你可以做到:
nonlocal mylist
mylist += [1]
用于更改某项内容或属性的Python 2替代方法是
def outer():
mylist = []
def inner(mylist = mylist):
mylist += [1]
inner()
outer()
如果要替换变量的值,则需要:
def outer():
def setlist(newlist):
mylist = newlist
mylist = []
def inner():
setlist(['new_list'])
inner()
outer()
答案 3 :(得分:1)
这是因为myList
内的inner()
未引用myList
中定义的outer()
,这就是为什么加号等于运算符不起作用。我想到了两种解决方案。
第一个,将myList
传递给内部作为参数:
def outer():
mylist = []
def inner(someList):
somelist += [1]
inner(mylist)
outer()
第二个解决方案是在两个函数之外声明myList,然后在两个函数中将其声明为global
:
mylist = []
def outer():
global mylist
mylist = []
def inner():
global mylist
mylist += [1]
inner()
outer()
虽然我会推荐第一个解决方案。