为什么Python + =(加上等于)运算符不能修改内部函数的变量?

时间:2011-07-22 23:29:32

标签: python list scope

我想知道为什么这不能按预期工作的细节:

def outer():
    mylist = []
    def inner():
        mylist += [1]

    inner()

outer()

特别是因为mylist.__iadd__([1])工作正常。

4 个答案:

答案 0 :(得分:10)

问题在于,当您在函数内部分配变量名时,Python会假设您正在尝试创建一个新的局部变量,该变量将掩盖外部作用域中类似命名的变量。由于+=必须获取 mylist的值才能修改它,因此会抱怨,因为尚未定义mylist的本地版本。 MRAB's answer给出了语义的清晰解释。

另一方面,当您执行mylist.__iadd__([1])时,您不会在函数内部分配新的变量名称。您只是使用内置方法来修改已分配的变量名称。只要您不尝试为mylist分配新值,就不会有问题。出于同样的原因,如果mylist[0] = 5inner的定义为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()

虽然我会推荐第一个解决方案。