在列表上使用 += 时出现 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 事件时)