Python中为什么变量需要nonlocal关键字而列表不需要?
核心原因:Python作用域判断逻辑只看「变量本身是否被赋值」,和你是否修改它指向的对象内容无关
Python在编译函数代码的时候,会扫描整个函数内的所有变量:
- 只要你在函数内任意位置对某个变量做了直接赋值(比如
a = xxx、a += xxx这种会改变变量本身指向的操作),又没有提前用global/nonlocal声明,Python就会默认把这个变量判定为当前函数的局部变量。 - 如果你只是读取变量的值,或者读取后修改它指向的可变对象的内部属性/元素,没有对变量本身做赋值,Python就不会把它标记为局部变量,会自动去外层作用域查找这个变量。
两种场景的差异解析
1. 为什么a += 1会报错
a += 1本质是对变量a本身的直接赋值,会改变a的指向。所以编译bar函数的时候,Python就把a标记成了bar的局部变量。运行时执行到这一行,需要先读取右边a的值做加法,但是此时局部变量a还没有被初始化赋值,自然就抛出UnboundLocalError。
2. 为什么l[0][0] -= 1不会报错
l[0][0] -= 1全程没有对变量l本身做任何赋值操作:
- 第一步是读取变量
l的值,拿到它指向的外层嵌套列表对象 - 第二步是修改这个列表对象内部的元素值
整个过程你没有给l赋新值,也没有改变l本身的指向,所以Python不会把l标记为chicken的局部变量,读取l的时候会正常去外层egg函数的作用域查找,当然不会报错。
拓展验证
如果你真的在chicken里对l本身做赋值,一样会触发错误,比如下面的代码就会抛UnboundLocalError:
def egg(): l = [[1 for _ in range(3)] for _ in range(3)] def chicken(): l = [] # 对l本身赋值,触发局部变量判定 l[0][0] -= 1 print(l) chicken() egg()
内容的提问来源于stack exchange,提问作者Subramanian Sridharan
相关产品推荐
相关产品推荐

