为什么Python的+=运算符不修改内部函数的变量?
我想了解一下,为什么这个没有按照预期工作:
def outer():
mylist = []
def inner():
mylist += [1]
inner()
outer()
特别是因为 mylist.__iadd__([1])
这个是正常的。
4 个回答
这是因为在 inner()
函数里面的 myList
并不是指向在 outer()
函数里定义的 myList
,所以加法赋值操作没有效果。我想到两个解决办法。
第一个办法是把 myList
作为参数传递给 inner
函数:
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()
不过我更推荐第一个办法。
如果在一个函数里给一个名字(变量)赋了值,那么这个名字就被当作局部变量,除非你特别声明它是全局的。
所以在inner
这个函数里,mylist
就是一个局部变量。
当你写x += y
的时候,Python在运行时会先尝试:
x = x.__iadd__(y)
如果这个尝试失败了,Python会接着尝试:
x = x.__add__(y)
问题在于,当你在一个函数内部给一个变量命名时,Python会认为你想创建一个新的局部变量,这个变量会遮盖外部作用域中同名的变量。因为 +=
需要先获取 mylist
的值才能进行修改,所以它会报错,因为局部的 mylist
还没有被定义。MRAB的回答 对这个问题的解释很清楚。
另一方面,当你使用 mylist.__iadd__([1])
时,你并没有在函数内部创建一个新的变量名。你只是用一个内置的方法来修改一个已经存在的变量名。只要你不尝试给 mylist
赋一个新值,就不会有问题。出于同样的原因,如果在 outer
中定义的 mylist
是 mylist = [1]
,那么在 inner
中, mylist[0] = 5
也能正常工作。
不过要注意,如果你在函数的任何地方尝试给 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
如果你想从外部作用域给一个变量赋新值,在 Python 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()