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

为什么 local_var 比 global_var 查找快?
因为 python 访问局部变量走的是栈帧的固定偏移寻址,而全局变量要查字典(globals()),后者涉及哈希计算和可能的冲突处理。CPython 在编译函数时就确定了每个局部变量在 f_locals 栈帧中的索引位置,运行时直接按索引取值,没有名字查找开销。
实操建议:
- 频繁读写的变量尽量定义在函数内,哪怕只是临时中转
- 不要为了“复用”把本该局部的变量提成模块级变量
- 用
dis.dis()看字节码能直观验证:局部变量对应LOAD_FAST,全局对应LOAD_GLOBAL
LOAD_FAST 和 LOAD_GLOBAL 的性能差距有多大?
不是“稍微快一点”,而是数量级差异。在纯访问场景下,LOAD_FAST 比 LOAD_GLOBAL 快 2–3 倍;如果变量被反复读写(比如循环里),差距会放大到 4–5 倍。这不是 CPU 缓存或解释器优化的偶然结果,而是底层指令路径完全不同。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
- 把配置常量全塞进
config.py,然后在函数里反复引用config.TIMEOUT—— 其实TIMEOUT = config.TIMEOUT提前赋值一次就够了 - 用
import math后在循环里写math.sqrt(x),不如先sqrt = math.sqrt
闭包变量 nonlocal 和 global 哪个更快?
nonlocal 变量比 global 快,但比纯 local 慢。它走的是“cell 对象间接寻址”:函数对象持有一个 __closure__ 元组,每个元素是 cell,里面装着对外层变量的引用。虽然多了一层解引用,但仍是 C 层直接指针操作,不触发字典查找。
使用场景与陷阱:
- 嵌套函数需要修改外层变量时,
nonlocal是唯一选择,别硬扛着用global - 如果只是读取外层变量,且不修改,Python 3.12+ 会尝试优化成只读闭包(类似局部),但老版本仍走 cell 路径
-
global在函数内首次赋值时会强制创建全局绑定,后续访问才走LOAD_GLOBAL,这个初始化过程有额外开销
哪些情况会让局部变量“变慢”?
局部变量快的前提是:它真被 Python 当作局部变量处理了。一旦出现某些语法或作用域干扰,编译器就会降级为 LOAD_GLOBAL 或更糟。
容易踩的坑:
- 函数内任何地方用了
exec()或eval(),整个函数所有变量都会退化为动态查找(包括原本的局部变量) - 写了
global x或nonlocal x,哪怕只针对一个变量,同名的其他变量也会失去局部优化机会 - 在函数开头用
from module import name,导入名会被当作局部变量,但若模块未加载成功,运行时报错时的回溯信息可能误导你认为它是“全局”
最隐蔽的点:Lambda 表达式里引用的外部变量,如果外层函数有 exec,那这个 lambda 的闭包变量也会失效 —— 这类问题很难通过肉眼发现,得靠 dis.dis 看实际指令。