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

nh2*_*nh2 5 python scope list

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

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

    inner()

outer()
Run Code Online (Sandbox Code Playgroud)

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

sen*_*rle 9

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

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

如果要为包含范围的变量分配新值,则可以使用3.0+ nonlocal,就像用于global为全局范围内的变量分配新值一样.所以不是这样的:

>>> mylist = []
>>> def inner():
...     global mylist
...     mylist += [1]
... 
>>> inner()
>>> mylist
[1]
Run Code Online (Sandbox Code Playgroud)

你做这个:

def outer():
    mylist = []
    def inner():
        nonlocal mylist
        mylist += [1]
    inner()
    print(mylist)
outer()
Run Code Online (Sandbox Code Playgroud)


MRA*_*RAB 6

如果在函数中绑定了一个名称(已赋值变量),则该名称将被视为local,除非它被声明为global.

因此inner,mylist是本地的.

当你写x += y,在运行时Python将尝试:

x = x.__iadd__(y)
Run Code Online (Sandbox Code Playgroud)

如果失败,Python会尝试:

x = x.__add__(y)
Run Code Online (Sandbox Code Playgroud)