You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中变量存在于locals()却无法在当前局部作用域访问

问题原因

这个现象是CPython解释器对函数局部变量的静态编译优化机制导致的,核心逻辑如下:

  • 编译期的局部变量确定规则
    Python在编译函数代码时,会静态扫描整个函数体内所有显式声明、赋值的变量,将这些变量标记为局部变量,运行时对局部变量的访问是通过栈帧上固定的偏移位置直接读取的,不会动态查询locals()返回的字典。
    示例中g()函数的代码里,除了exec内的字符串代码,没有任何地方显式定义或赋值过变量b,编译器编译阶段根本不会将b识别为该函数的局部变量,不会为它分配栈帧上的存储槽位。
  • exec()的实际写入位置
    函数内调用exec()且没有手动指定命名空间参数时,exec()会默认使用当前函数的locals()返回字典作为代码执行的局部命名空间,因此执行exec("b=1")时,确实将b:1这个键值对写入了locals()字典中,这也是为什么你能通过locals()['b']成功取出值1。
  • 直接访问报错的触发逻辑
    当代码执行到print(b)时,解释器按照编译阶段确定的规则查找b:首先检查当前函数的局部变量槽位,没有找到b;接着去全局命名空间、内置命名空间查找,依然没有找到b,最终抛出NameError。

补充说明:这个现象仅出现在函数作用域中。如果在模块全局作用域执行相同的exec("b=1")再直接访问b是可以正常运行的——因为全局变量的访问本身就是动态查询globals()字典实现的,没有编译期静态绑定局部变量槽位的优化。
另外官方文档明确标注:不建议手动修改locals()返回的字典,修改后的行为在不同解释器、不同版本中不做一致性保证,CPython中函数内的locals()字典本质是栈帧局部变量的快照,对字典的修改不会反向同步到栈帧的局部变量槽位中。

复现代码与运行结果

复现代码:

def g():
    print("Locals:", locals().keys())
    exec("b=1")
    print("Locals:", locals().keys())
    print(locals()['b'])
    print(b)

g()

运行输出:

Locals: dict_keys([])
Locals: dict_keys(['b'])
1
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    g()
  File "test.py", line 8, in g
    print(b)
NameError: name 'b' is not defined

可行的规避方案

如果需要在函数内通过exec动态生成变量后访问,有两种通用方案:

  • 提前在函数内显式声明变量,让编译器将其识别为局部变量:
    def g():
        b = None  # 提前声明,编译器会为b分配局部变量槽位
        exec("b=1")
        print(b)  # 正常输出1
    
  • 给exec()传入独立的命名空间字典,不要依赖函数局部命名空间:
    def g():
        ns = {}
        exec("b=1", ns)
        print(ns['b'])  # 正常输出1
    

内容的提问来源于stack exchange,提问作者Frank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:09:27