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

Posted

技术标签:

【中文标题】在列表上使用 += 时出现 UnboundLocalError。为啥直接调用 __iadd__ 时这里需要 `nonlocal` 可以正常工作?【英文标题】:UnboundLocalError when using += on list. Why is `nonlocal` needed here when directly calling __iadd__ works fine?在列表上使用 += 时出现 UnboundLocalError。为什么直接调用 __iadd__ 时这里需要 `nonlocal` 可以正常工作? 【发布时间】:2017-10-21 18:26:39 【问题描述】:

考虑这段代码:

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__ 而不是+=

问题:为什么这里需要nonlocal

这让我很惊讶。

【问题讨论】:

调用__iadd__的返回值会在哪里赋值? 【参考方案1】:

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

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

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

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

这两种翻译还需要nonlocal 语句,因为它们都访问l 并分配回l

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

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

list.__iadd__(),在后台调用list.extend(),然后返回self

【讨论】:

【参考方案2】:

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

l = l + [1]

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

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

【讨论】:

以上是关于在列表上使用 += 时出现 UnboundLocalError。为啥直接调用 __iadd__ 时这里需要 `nonlocal` 可以正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

Django - 在 javascript 函数中使用 和 % % 标签时出现语法错误

尝试使用 struts 中的 ajax 更新 jsp 页面上的内容时出现问题(触发 onChange 事件时)

从 api 获取数据时出现空白屏幕

在地图视图上使用轨迹位置时出现可变错误

使用 Smack 在 ejabberd 上创建用户时出现异常

为啥在使用 readlines() 读取文件时出现列表索引超出范围错误?