局部变量用LOAD_FAST(索引直取),全局变量用LOAD_GLOBAL(哈希查字典),慢2–5倍;可用dis.dis()验证:操作数为数字索引是LOAD_FAST,字符串则为LOAD_GLOBAL。

因为局部变量走的是栈帧固定索引直取(LOAD_FAST),全局变量每次都要哈希查字典(LOAD_GLOBAL),底层指令路径完全不同,实测慢 2–5 倍。
怎么验证是 LOAD_FAST 还是 LOAD_GLOBAL?
用 dis.dis() 看字节码,一眼就能区分:
-
LOAD_FAST 0→ 操作数是数字索引,说明是局部变量 -
LOAD_GLOBAL 'math'或LOAD_GLOBAL 'config'→ 操作数是字符串,说明走全局字典查找 - 哪怕只在函数里写了一次
import math,后续所有math.sqrt都触发LOAD_GLOBAL,不是只查一次
哪些写法会让局部变量“失效”,退化成全局查找?
Python 编译期就决定变量是否局部,以下情况会强制降级:
- 赋值前读取同名变量:
print(x); x = 1→ 触发UnboundLocalError或隐式全局引用 - 用了
exec()、eval()或locals()→ 解释器放弃fastlocals优化,全部退化为字典查找 - 显式声明
global x→ 读写都绕过局部作用域,哪怕只是读,也走LOAD_GLOBAL - 闭包中用
nonlocal x→ 走LOAD_DEREF,性能居中,比LOAD_GLOBAL快但不如LOAD_FAST
高频循环里怎么改才真有效?
不是所有地方都值得动,只在真正被反复访问的路径上做——比如每秒调用上千次的循环体:
立即学习“Python免费学习笔记(深入)”;
- 把模块函数拉成局部变量:
sqrt = math.sqrt,再在循环里用sqrt(x) - 把配置项提前赋值:
timeout = config.TIMEOUT,别每次都点config.TIMEOUT - 把顶层脚本逻辑包进函数:连
for i in range(N)里的i都自动变局部,省掉每次循环对range对象的属性查找 - 别为了快把大对象(如
pandas.DataFrame)重复传参——内存拷贝开销可能远超名字查找收益
最容易被忽略的一点:这个加速只对编译期能静态确定作用域的名称生效。通过 getattr(obj, 'attr')、obj.__dict__['attr'] 或类属性访问,完全不走 LOAD_FAST,也优化不了。


















