Python 局部变量访问更快的原因

1次阅读

local_var 比 global_var 查找快,因前者通过帧固定偏移直接寻址(load_fast),后者需哈希查字典(load_global),性能差距达2–5倍;nonlocal居中,走cell间接寻址;exec/eval、global/nonlocal声明或异常导入会使局部变量退化为全局查找。

Python 局部变量访问更快的原因

为什么 local_varglobal_var 查找快?

因为 python 访问局部变量走的是栈帧的固定偏移寻址,而全局变量要查字典(globals()),后者涉及哈希计算和可能的冲突处理。CPython 在编译函数时就确定了每个局部变量在 f_locals 栈帧中的索引位置,运行时直接按索引取值,没有名字查找开销。

实操建议:

  • 频繁读写的变量尽量定义在函数内,哪怕只是临时中转
  • 不要为了“复用”把本该局部的变量提成模块级变量
  • dis.dis() 看字节码能直观验证:局部变量对应 LOAD_FAST,全局对应 LOAD_GLOBAL

LOAD_FASTLOAD_GLOBAL 的性能差距有多大?

不是“稍微快一点”,而是数量级差异。在纯访问场景下,LOAD_FASTLOAD_GLOBAL 快 2–3 倍;如果变量被反复读写(比如循环里),差距会放大到 4–5 倍。这不是 CPU 缓存或解释器优化的偶然结果,而是底层指令路径完全不同。

常见错误现象:

立即学习Python免费学习笔记(深入)”;

  • 把配置常量全塞进 config.py,然后在函数里反复引用 config.TIMEOUT —— 其实 TIMEOUT = config.TIMEOUT 提前赋值一次就够了
  • import math 后在循环里写 math.sqrt(x),不如先 sqrt = math.sqrt

闭包变量 nonlocalglobal 哪个更快?

nonlocal 变量比 global 快,但比纯 local 慢。它走的是“cell 对象间接寻址”:函数对象持有一个 __closure__ 元组,每个元素是 cell,里面装着对外层变量的引用。虽然多了一层解引用,但仍是 C 层直接指针操作,不触发字典查找。

使用场景与陷阱:

  • 嵌套函数需要修改外层变量时,nonlocal 是唯一选择,别硬扛着用 global
  • 如果只是读取外层变量,且不修改,Python 3.12+ 会尝试优化成只读闭包(类似局部),但老版本仍走 cell 路径
  • global 在函数内首次赋值时会强制创建全局绑定,后续访问才走 LOAD_GLOBAL,这个初始化过程有额外开销

哪些情况会让局部变量“变慢”?

局部变量快的前提是:它真被 Python 当作局部变量处理了。一旦出现某些语法或作用域干扰,编译器就会降级为 LOAD_GLOBAL 或更糟。

容易踩的坑:

  • 函数内任何地方用了 exec()eval(),整个函数所有变量都会退化为动态查找(包括原本的局部变量)
  • 写了 global xnonlocal x,哪怕只针对一个变量,同名的其他变量也会失去局部优化机会
  • 在函数开头用 from module import name,导入名会被当作局部变量,但若模块未加载成功,运行时报错时的回溯信息可能误导你认为它是“全局”

最隐蔽的点:Lambda 表达式里引用的外部变量,如果外层函数有 exec,那这个 lambda 的闭包变量也会失效 —— 这类问题很难通过肉眼发现,得靠 dis.dis 看实际指令。

text=ZqhQzanResources