Python高阶函数何时需要使用nonlocal关键字?
nonlocal关键字的使用规则 核心判断逻辑完全基于Python的静态作用域规则,和变量是不是在函数外部调用没有关系:
- 当你在嵌套定义的内层函数中,需要对外层非全局作用域的变量做重新赋值操作时,必须使用
nonlocal声明该变量,否则Python会默认将其识别为当前内层函数的局部变量,触发变量未定义的报错。 - 如果只是读取外层作用域的变量值,从来不对变量本身做重新赋值,不需要加任何声明,Python会自动沿着作用域链向上查找匹配的变量。
为什么代码1必须加nonlocal声明
代码1的内层say函数中存在对previous_score的直接赋值语句:
previous_score = max(previous_score, current_score)
如果没有nonlocal声明,Python解析函数的时候,会把所有出现在赋值符号左边的变量判定为当前函数的局部变量。这时候执行到赋值语句右边取previous_score值的时候,这个局部变量还没完成赋值,就会抛出UnboundLocalError: local variable 'previous_score' referenced before assignment错误,代码根本无法运行。
加上nonlocal previous_score后,Python就会明确:这个变量不属于当前say函数的局部作用域,去外层announce_max的作用域中查找对应变量,赋值操作也是修改外层作用域的变量值,而非新建局部变量,逻辑才能正常跑通。
为什么代码2不需要nonlocal就能运行
看代码2的say函数逻辑,全程没有对previous_score做任何赋值操作,所有用到这个变量的地方都是单纯读取它的值:
# 只是读取previous_score的值传给max计算,没有修改变量本身 print(max(previous_score, current_score)) # 同样是读值计算,把结果作为参数传给新的announce_max调用,没有给previous_score赋值 return announce_max(max(previous_score, current_score))
这种只读不写的场景下,Python不会把previous_score识别为局部变量,会自动向上找到外层announce_max函数的参数previous_score取值,因此不需要额外声明就能正常运行。
常见误区提醒:不要混淆「修改可变对象内部内容」和「给变量重新赋值」。比如外层变量是一个字典
d = {},内层写d['score'] = 1是修改字典内部的键值对,不是给d变量本身重新赋值,不需要加nonlocal;但如果内层写d = {'score':1},就是给变量重新赋值,必须加nonlocal声明才能修改外层的d变量。
内容的提问来源于stack exchange,提问作者user19402456

