为何内部函数引用局部变量会改变Python locals()的返回顺序?
为什么Python 3.10及更早版本中,局部变量顺序会因内部引用改变?
先看正常场景的代码,执行后会按参数定义顺序输出局部变量:
def foo(a: str = "a", b: str = "b") -> None: print(locals()) foo() # 输出 {'a': 'a', 'b': 'b'}
这符合Python 3.7+的预期——locals()会按变量创建的顺序返回结果。
但如果在函数内部添加对某个参数的闭包引用(比如lambda或内部函数引用参数a),输出顺序就会反转:
def foo(a: str = "a", b: str = "b") -> None: print(locals()) lambda: a # 或者替换为内部函数: # def inner() -> None: # a foo() # 输出 {'b': 'b', 'a': 'a'}
这个现象仅出现在Python 3.10及更低版本,3.11及以上版本已修复。
原因分析
这是Python解释器处理闭包时的一个实现细节问题:
当函数内部出现对局部变量的闭包引用时,解释器会为被引用的变量创建cell对象(用于在闭包间共享变量)。在这个过程中,解释器会调整局部变量的内部存储顺序:被闭包引用的变量会被移到局部变量列表的末尾,未被引用的变量则保留在原有位置。
在示例中,参数a被lambda/内部函数引用,因此被移到了局部变量列表的最后;b没有被引用,排在前面。而locals()返回的字典顺序会跟随这个内部存储顺序,最终就出现了b在前、a在后的输出结果。
Python 3.11对局部变量的存储逻辑和闭包处理机制做了优化,修复了这个顺序异常的问题,所以在新版本中无论是否存在闭包引用,locals()都会保持参数定义的原始顺序。
内容的提问来源于stack exchange,提问作者Nelson Yeung
相关产品推荐
相关产品推荐

