在列表上使用 += 时出现 UnboundLocalError。为什么直接调用 __iadd__ 就可以正常工作,这里需要 `nonlocal`?

UnboundLocalError when using += on list. Why is `nonlocal` needed here when directly calling __iadd__ works fine?

考虑这段代码:

def main():
    l = []

    def func():
        l += [1]

    func()
    print(l)

if __name__ == '__main__':
    main()

它将产生:

Traceback (most recent call last):
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 14, in <module>
    main()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 11, in main
    func()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 9, in func
    l += [1]
UnboundLocalError: local variable 'l' referenced before assignment

这本身可以通过在 func 开头使用 nonlocal l 直接使用 __iadd__ 而不是 [=16] 来解决=].

问题:为什么这里需要nonlocal

这让我很惊讶。

+= 是增强的 assignment 运算符;它大致翻译为:

def func():
    l = l + [1]

如果您要用对 object.__iadd__() 的调用替换 l += [1],如果您要使用它,则不能忽略该调用的 return 值 正确:

def func():
    l = l.__iadd__([1])

这两个翻译也需要一个 nonlocal 语句,因为它们都访问 l 并分配回 l.

您可以忽略 object.__iadd__ 的 return 值,因为列表对象是可变的;该列表已就地更改。但是在那种情况下你也可以使用 list.extend() 调用:

def func():
    l.extend([1])

list.__iadd__(),在幕后,在 returning self.

之前调用 list.extend()

因为,在幕后,l += [1] 导致:

l = l + [1]

在分配给它之前引用了名称 l;这就是为什么你会得到 UnboundLocalError

l.__iadd__,另一方面,是一个简单的函数调用;它 执行赋值,因此不需要 nonlocal 来帮助查找名称 l.