Python嵌套函数中如何为特定函数绑定nonlocal变量
问题1:为什么最内层
vnat修改x后,outer打印的x是5? 核心原因是Python的nonlocal变量解析规则:
- nonlocal会从当前函数的上一层作用域开始,逐层向上查找第一个真实存在的、不属于全局作用域的同名变量,找到后直接绑定到该变量本体,不存在强制的层级隔断。
- 回到原代码的作用域层级:
- 最外层全局作用域:
x = 0 outer作用域:定义了局部变量x = 1inner作用域:因为一上来就声明了nonlocal x,按照Python的静态作用域规则,inner的整个作用域都不会创建属于自己的x局部变量,所有对x的引用都直接绑定到outer作用域的x。换句话说,inner的命名空间里根本不存在x这个键。vnat作用域:声明nonlocal x的时候,从自己的上一层(也就是inner的命名空间)开始查找x,发现inner根本没有x,就会继续向上跳转一层,找到outer作用域里的x,直接绑定。
- 最外层全局作用域:
所以vnat里执行x = 5,本质是直接修改outer作用域里的x变量本体。等vnat执行完,inner打印x的时候拿到的是被改过的outer的x=5,回到outer打印自己的x时,自然也是被修改后的5。
常见误区:很多人以为
inner里的nonlocal x会生成一个属于inner的独立变量,实际上nonlocal只是把当前函数的变量解析指向外层已有的变量本体,不会新建任何独立的存储副本。
问题2:如何实现目标输出(
outer读取到x=2) 要达到预期,核心是让vnat的nonlocal x绑定到inner自己的独立局部x,而不是跳过inner找到outer的x,同时还要保留inner把outer的x修改为2的逻辑(这样outer打印的时候才能拿到2)。
具体改法是调整嵌套结构,让inner拥有自己的x命名空间,参考代码如下:
x = 0 def outer(): x = 1 def inner(): # 定义辅助函数专门修改outer的x def set_outer_x(): nonlocal x x = 2 # 调用辅助函数把outer的x改为2 set_outer_x() # inner本身没有针对x的nonlocal声明,可以定义独立的局部x x = 2 def vnat(): nonlocal x x = 5 print('vnat:', x) vnat() print('inner:', x) inner() print('outer:', x) outer() print('global:', x)
改完后的执行效果:
inner本身没有声明针对x的nonlocal,所以可以在自己的命名空间里创建独立的x=2,不会干扰外层的变量解析。- 内层的
set_outer_x辅助函数通过自己的nonlocal绑定到outer的x,调用后把outer的x从1改成2,保证outer打印的时候能拿到2。 vnat的nonlocal向上查找时,会直接找到inner自己的x变量,后续对x的修改只会作用在inner的局部x上,不会触碰outer的x。- 实际运行输出完全匹配预期:
vnat: 5 inner: 5 outer: 2 global: 0
内容的提问来源于stack exchange,提问作者robo_subhuman
相关产品推荐
相关产品推荐

