bigywater.tech
Python · 21

Python 字节码:dis 看到的指令是怎么执行的

用dis.dis反编译一个只有一行return x+1的函数,看CPython 3.14把这行代码拆成了哪些指令,字面量又是怎么被处理的。

5条指令
拆解一行return x+1
LOAD_SMALL_INT
针对小整数字面量的专用指令
版本相关
字节码是解释器实现细节,不是语言规范

做的事:反编译一个最简单的函数

Python源码不是直接被解释器执行的,中间先编译成一份字节码。

代码def add_one(x): return x + 1 import dis dis.dis(add_one)

验证:一行代码拆成了4条指令

的反编译输出。

dis.dis输出 5 RESUME 0 6 LOAD_FAST_BORROW 0 (x) LOAD_SMALL_INT 1 BINARY_OP 0 (+) RETURN_VALUE

拆解:RESUME 是函数开始执行时的一个占位指令(给解释器插桩用);LOAD_FAST_BORROW 从局部变量槽里取出参数x("Borrow"表示这次取值不需要增加引用计数,是Python 3.14里对LOAD_FAST的一种优化);LOAD_SMALL_INT 把字面量1推入栈——这条指令本身就很说明问题。

验证:字面量1没有走常量表,co_consts是空的对应位

按常理,字面量应该存在co_consts里,用LOAD_CONST取出来。

结果add_one.__code__.co_consts = (1,) add_one.__code__.co_names = () add_one.__code__.co_stacksize = 2 add_one.__code__.co_nlocals = 1

co_consts里确实还留着(1,)这个元组,但字节码里用的是 LOAD_SMALL_INT 而不是 LOAD_CONST 0 (1)——这是较新CPython版本做的一项优化:对于能装进一个机器字的小整数,解释器会用专门的LOAD_SMALL_INT指令直接把整数值编码在指令参数里,运行时不用再去co_consts元组里做一次索引查找,省下一次的内存访问。co_stacksize=2 说明这4条指令执行过程中,操作数栈最多同时压了2个值(x和1);co_nlocals=1 对应只有一个局部变量x。

这解释了为什么反编译一段"看起来一样"的Python代码,在不同版本的CPython里会得到不同的指令序列——字节码不是Python语言本身的规范,而是某一个具体版本解释器的内部实现细节,dis.dis看到的永远是"这个版本的CPython选择怎么执行这段代码",而不是Python语言标准规定的执行方式。

分步演示:字节码是怎么描述这次加法的

每一步都是本文上面用dis模块跑出来的原始结果。

一行return x+1拆成的5条字节码指令
这一步新验证的结果

    本文用到的工具

    • 本机 Python 3.14.7
    • dis.dis / __code__.co_consts:反编译并检查字节码与常量表
    京ICP备2026067502号-1京公网安备11010502063487号