当前位置:首页>python>解密Python字节码:第四列那个神秘数字到底代表什么

解密Python字节码:第四列那个神秘数字到底代表什么

  • 2026-10-21 13:27:50
解密Python字节码:第四列那个神秘数字到底代表什么
解密Python字节码:第四列那个神秘数字到底代表什么

TLDR:第 4 列数字没有固定含义。其是opcode 的操作数(oparg),含义完全由该 opcode 决定,可能是索引、计数、枚举或位编码。

Python不同版本字节码有变化,本文使用3.13环境。仍然用上一篇文章的5行示例代码:

x = 1def foo():    print(x)    x += 1foo()

dis反编译字节码:

偏移量  源码行号  OPCODE  oparg (dis反查出人类可读解释)   0   RESUME           0  1   LOAD_CONST       0 (1)      STORE_NAME       0 (x)  2   LOAD_CONST       1 (<code object foo at 0x60..., file "e.py", line 2>)      MAKE_FUNCTION      STORE_NAME       1 (foo)  5   LOAD_NAME        1 (foo)      PUSH_NULL      CALL             0      POP_TOP      RETURN_CONST     2 (None)foo  2   RESUME           0  3   LOAD_GLOBAL      1 (print + NULL)      LOAD_FAST_CHECK  0 (x)      CALL             1      POP_TOP  4   LOAD_FAST        0 (x)      LOAD_CONST       1 (1)      BINARY_OP       13 (+=)      STORE_FAST       0 (x)      RETURN_CONST     0 (None)

Python 3.13 的 dis 输出中,第 4 列的数字是该指令的 操作数(oparg)。 它不是“第几行”“第几条指令”,而是 紧跟在 opcode 后面的参数值,其语义 完全由具体指令决定。

这份输出里,其实每一列含义是固定的:

偏移量  源码行号  OPCODE   oparg   (人类可读解释)

一、列结构澄清(Python 3.13)

以这一行为例:

3   LOAD_GLOBAL      1 (print + NULL)
列
含义
3
源码行号(line number table 映射结果)
LOAD_GLOBAL
opcode 名称
1
oparg(操作数)
(print + NULL)
dis 根据 oparg 解析出来的语义

真正的“第 4 列数字”是 1,它是 oparg。

二、oparg 的本质是什么?

oparg 是“指令参数”,但它不是统一语义的数字。

oparg 的意义 = 由 opcode 决定

它通常是以下几类之一:

类型
说明
索引
指向 co_consts / co_names / co_varnames / co_cellvars
计数
参数个数(如 CALL)
枚举值
操作类型(如 BINARY_OP)
位编码
多个标志位打包在一起

三、结合例子逐条解释

LOAD_CONST 0 (1)

LOAD_CONST 0 (1)
  • • 0 → co_consts[0]
  • • (1) 是 dis 反查出来的值

等价于:

push(co_consts[0])   // 1

STORE_NAME 0 (x)

STORE_NAME 0 (x)
  • • 0 → co_names[0]
  • • (x) 是名字表里的第 0 项

LOAD_GLOBAL 1 (print + NULL)

LOAD_GLOBAL 1 (print + NULL)

这里是 Python 3.11+ 的重要变化点。

oparg = 1 表示什么?

  • • oparg >> 1 → co_names 索引
  • • oparg & 1 → 是否需要 NULL(用于 vectorcall)

也就是:

name_index = 1 >> 1 = 0push_null  = 1 & 1  = 1

所以:

(print + NULL)

这是 调用路径优化的一部分。

LOAD_FAST_CHECK 0 (x)

LOAD_FAST_CHECK 0 (x)
  • • 0 → co_varnames[0]
  • • CHECK = 如果未绑定,抛 UnboundLocalError

这是笔者上篇文章那个:

print(x)x += 1

会报错的根本原因之一。

CALL 1

CALL 1
  • • 1 = 位置参数个数
  • • 不包括关键字参数(它们在栈上)

BINARY_OP 13 (+=)

BINARY_OP 13 (+=)
  • • 13 是一个 枚举值
  • • 对应内部表:
NB_INPLACE_ADD

不是 magic number,是 CPython 内部定义的操作码映射。

四、为什么 Python 3.13 的 dis 看起来“数字变多了”?

原因是 CPython 3.11+ 引入了统一的 wordcode + specializing interpreter:

  • • 很多指令被合并(如 BINARY_ADD → BINARY_OP)
  • • oparg 变成 多语义复用字段
  • • 一个整数可能编码多个信息

最新文章

随机文章