dis --- Python 字节码反汇编器

源代码: Lib/dis.py


dis 模块通过反汇编 CPython 的 字节码 来支持对它的分析。该模块接受的 CPython 字节码输入在文件 Include/opcode.h 中定义,并被编译器和解释器使用。

CPython 实现细节: 字节码是 CPython 解释器的一种实现细节。不保证字节码不会在不同 Python 版本之间被添加、移除或更改。不应假定此模块的运用可在不同 Python 虚拟机或 Python 发布版之间保持有效。

在 3.6 版更改: 每条指令使用 2 个字节。以前字节数因指令而异。

在 3.10 版更改: 跳转、异常处理和循环指令的参数现在是指令偏移量,而不是字节偏移量。

在 3.11 版更改: 某些指令会附带一个或多个内联缓存条目,其形式为 CACHE 指令。这些指令默认被隐藏,但可以通过向任何 dis 工具函数传入 show_caches=True 来显示。此外,解释器现在会调整字节码以针对不同的运行时条件进行特化。特化后的自适应字节码可以通过传入 adaptive=True 来显示。

在 3.12 版更改: 跳转指令的参数是目标指令相对于跳转指令的 CACHE 条目之后紧邻的指令的偏移量。

因此,CACHE 指令的存在对前向跳转是透明的,但在分析后向跳转时需要予以考虑。

在 3.13 版更改: 输出会为跳转目标和异常处理程序显示逻辑标签,而非指令偏移量。添加了 -O 命令行选项和 show_offsets 参数。

在 3.14 版更改: 添加了 -P 命令行选项和 show_positions 参数。

添加了 -S 命令行选项。

示例:对于函数 myfunc()

def myfunc(alist):
    return len(alist)

可以使用以下命令来显示 myfunc() 的反汇编代码

>>> dis.dis(myfunc)
  2           RESUME                   0

  3           LOAD_GLOBAL              1 (len + NULL)
              LOAD_FAST_BORROW         0 (alist)
              CALL                     1
              RETURN_VALUE

(“2”是行号)。

命令行界面

dis 模块可以作为脚本从命令行调用

python -m dis [-h] [-C] [-O] [-P] [-S] [infile]

接受以下选项

-h, --help

显示用法并退出。

-C, --show-caches

显示内联缓存。

在 3.13 版本加入。

-O, --show-offsets

显示指令的偏移量。

在 3.13 版本加入。

-P, --show-positions

显示指令在源代码中的位置。

在 3.14 版本加入。

-S, --specialized

显示特化的字节码。

在 3.14 版本加入。

如果指定了 infile,其反汇编代码将被写入 stdout。否则,反汇编将对从 stdin 接收的已编译源代码执行。

字节码分析

在 3.4 版本加入。

字节码分析 API 允许将 Python 代码片段包装在一个 Bytecode 对象中,该对象提供了对已编译代码细节的便捷访问。

class dis.Bytecode(x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False)

分析一个函数、生成器、异步生成器、协程、方法、源代码字符串或代码对象(由 compile() 返回)对应的字节码。

这是对下面列出的许多函数的一个方便的包装,尤其是 get_instructions(),因为迭代一个 Bytecode 实例会产生字节码操作的 Instruction 实例。

如果 first_line 不为 None,它表示在反汇编代码中为第一行源代码报告的行号。否则,源代码行信息(如果有)直接从反汇编的代码对象中获取。

如果 current_offset 不为 None,它指向反汇编代码中的一个指令偏移量。设置此值意味着 dis() 将在指定的 opcode 旁边显示一个“当前指令”标记。

如果 show_cachesTruedis() 将显示解释器用来特化字节码的内联缓存条目。

如果 adaptiveTruedis() 将显示可能与原始字节码不同的特化字节码。

如果 show_offsetsTruedis() 将在输出中包含指令偏移量。

如果 show_positionsTruedis() 将在输出中包含指令的源代码位置。

classmethod from_traceback(tb, *, show_caches=False)

根据给定的回溯信息构造一个 Bytecode 实例,并将 current_offset 设置为导致异常的指令。

codeobj

已编译的代码对象。

first_line

代码对象的第一个源代码行号(如果可用)。

dis()

返回一个格式化的字节码操作视图(与 dis.dis() 打印的内容相同,但作为多行字符串返回)。

info()

返回一个格式化的多行字符串,其中包含有关代码对象的详细信息,类似于 code_info()

在 3.7 版更改: 现在可以处理协程和异步生成器对象。

在 3.11 版更改: 添加了 show_cachesadaptive 参数。

在 3.13 版更改: 添加了 show_offsets 参数

在 3.14 版更改: 添加了 show_positions 参数。

示例

>>> bytecode = dis.Bytecode(myfunc)
>>> for instr in bytecode:
...     print(instr.opname)
...
RESUME
LOAD_GLOBAL
LOAD_FAST_BORROW
CALL
RETURN_VALUE

分析函数

dis 模块还定义了以下分析函数,可将输入直接转换为所需输出。如果只执行单个操作,因此中间分析对象没有用处,它们可能会很有用。

dis.code_info(x)

为提供的函数、生成器、异步生成器、协程、方法、源代码字符串或代码对象返回一个格式化的多行字符串,其中包含详细的代码对象信息。

请注意,代码信息字符串的确切内容高度依赖于具体实现,并且可能在不同的 Python 虚拟机或 Python 发布版本之间任意更改。

在 3.2 版本加入。

在 3.7 版更改: 现在可以处理协程和异步生成器对象。

dis.show_code(x, *, file=None)

将提供的函数、方法、源代码字符串或代码对象的详细代码对象信息打印到 file(如果未指定 file,则为 sys.stdout)。

这是 print(code_info(x), file=file) 的一个方便的简写形式,旨在用于解释器提示符下的交互式探索。

在 3.2 版本加入。

在 3.4 版更改: 添加了 file 参数。

dis.dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False)

反汇编 x 对象。x 可以表示模块、类、方法、函数、生成器、异步生成器、协程、代码对象、源代码字符串或原始字节码的字节序列。对于模块,它反汇编所有函数。对于类,它反汇编所有方法(包括类方法和静态方法)。对于代码对象或原始字节码序列,它为每个字节码指令打印一行。它还会递归地反汇编嵌套的代码对象。这些可以包括生成器表达式、嵌套函数、嵌套类的主体以及用于 注解作用域 的代码对象。字符串首先使用 compile() 内置函数编译为代码对象,然后再进行反汇编。如果没有提供对象,此函数将反汇编上一个回溯信息。

如果提供了 file 参数,反汇编结果将作为文本写入该文件,否则写入 sys.stdout

最大递归深度由 depth 限制,除非它为 Nonedepth=0 表示不递归。

如果 show_cachesTrue,此函数将显示解释器用于特化字节码的内联缓存条目。

如果 adaptiveTrue,此函数将显示可能与原始字节码不同的特化字节码。

在 3.4 版更改: 添加了 file 参数。

在 3.7 版更改: 实现了递归反汇编并添加了 depth 参数。

在 3.7 版更改: 现在可以处理协程和异步生成器对象。

在 3.11 版更改: 添加了 show_cachesadaptive 参数。

在 3.13 版更改: 添加了 show_offsets 参数。

在 3.14 版更改: 添加了 show_positions 参数。

dis.distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offset=False, show_positions=False)

反汇编回溯信息中的栈顶函数,如果未传入则使用最后的回溯信息。导致异常的指令会被指出。

如果提供了 file 参数,反汇编结果将作为文本写入该文件,否则写入 sys.stdout

在 3.4 版更改: 添加了 file 参数。

在 3.11 版更改: 添加了 show_cachesadaptive 参数。

在 3.13 版更改: 添加了 show_offsets 参数。

在 3.14 版更改: 添加了 show_positions 参数。

dis.disassemble(code, lasti=-1, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False)
dis.disco(code, lasti=-1, *, file=None, show_caches=False, adaptive=False, show_offsets=False, show_positions=False)

反汇编一个代码对象,如果提供了 lasti,则指示最后一条指令。输出分为以下几列:

  1. 指令的源代码位置。如果 show_positions 为 true,则显示完整的位置信息。否则(默认)只显示行号。

  2. 当前指令,用 --> 表示,

  3. 一个带标签的指令,用 >> 表示,

  4. 指令的地址,

  5. 操作码名称,

  6. 操作参数,以及

  7. 括号中对参数的解释。

参数解释能识别局部和全局变量名、常量值、分支目标和比较运算符。

如果提供了 file 参数,反汇编结果将作为文本写入该文件,否则写入 sys.stdout

在 3.4 版更改: 添加了 file 参数。

在 3.11 版更改: 添加了 show_cachesadaptive 参数。

在 3.13 版更改: 添加了 show_offsets 参数。

在 3.14 版更改: 添加了 show_positions 参数。

dis.get_instructions(x, *, first_line=None, show_caches=False, adaptive=False)

返回一个迭代器,用于遍历提供的函数、方法、源代码字符串或代码对象中的指令。

迭代器生成一系列 Instruction 命名元组,给出所提供代码中每个操作的详细信息。

如果 first_line 不为 None,它表示在反汇编代码中为第一行源代码报告的行号。否则,源代码行信息(如果有)直接从反汇编的代码对象中获取。

adaptive 参数的作用与在 dis() 中一样。

在 3.4 版本加入。

在 3.11 版更改: 添加了 show_cachesadaptive 参数。

在 3.13 版更改: show_caches 参数已弃用且不起作用。迭代器会生成填充了 cache_info 字段的 Instruction 实例(无论 show_caches 的值如何),并且不再为缓存条目生成单独的项。

dis.findlinestarts(code)

这个生成器函数使用 代码对象 codeco_lines() 方法来查找源代码中行开始的偏移量。它们以 (offset, lineno) 对的形式生成。

在 3.6 版更改: 行号可以递减。以前,它们总是递增的。

在 3.10 版更改: 使用了 PEP 626co_lines() 方法,取代了 代码对象co_firstlinenoco_lnotab 属性。

在 3.13 版更改: 对于不映射到源代码行的字节码,行号可以为 None

dis.findlabels(code)

检测原始编译字节码字符串 code 中所有作为跳转目标的偏移量,并返回这些偏移量的列表。

dis.stack_effect(opcode, oparg=None, *, jump=None)

计算带参数 opargopcode 的堆栈效应。

如果代码有跳转目标且 jumpTruestack_effect() 将返回跳转的堆栈效应。如果 jumpFalse,它将返回不跳转的堆栈效应。如果 jumpNone(默认值),它将返回两种情况下的最大堆栈效应。

在 3.4 版本加入。

在 3.8 版更改: 添加了 jump 参数。

在 3.13 版更改: 如果省略 oparg(或为 None),现在将返回 oparg=0 的堆栈效应。以前,对于使用参数的操作码,这会引发错误。当 opcode 不使用参数时,传递一个整数 oparg 也不再是错误;在这种情况下,oparg 会被忽略。

Python 字节码指令

get_instructions() 函数和 Bytecode 类以 Instruction 实例的形式提供字节码指令的详细信息。

class dis.Instruction

字节码操作的详细信息

opcode

操作的数字代码,对应于下面列出的操作码值和 操作码集合 中的字节码值。

opname

人类可读的操作名称

baseopcode

如果操作是特化的,则为基础操作的数字代码;否则等于 opcode

baseopname

如果操作是特化的,则为基础操作的人类可读名称;否则等于 opname

arg

操作的数字参数(如果有),否则为 None

oparg

arg 的别名

argval

解析后的参数值(如果有),否则为 None

argrepr

操作参数的人类可读描述(如果有),否则为空字符串。

offset

操作在字节码序列中的起始索引

start_offset

操作在字节码序列中的起始索引,包括前缀的 EXTENDED_ARG 操作(如果存在);否则等于 offset

cache_offset

操作后缓存条目的起始索引

end_offset

操作后缓存条目的结束索引

starts_line

如果此操作码开始一个源代码行,则为 True,否则为 False

line_number

与此操作码关联的源代码行号(如果有),否则为 None

is_jump_target

如果其他代码跳转到此处,则为 True,否则为 False

jump_target

如果这是一个跳转操作,则为跳转目标的字节码索引,否则为 None

positions

dis.Positions 对象,包含此指令覆盖的起始和结束位置。

cache_info

关于此指令缓存条目的信息,形式为 (name, size, data) 的三元组,其中 namesize 描述缓存格式,data 是缓存的内容。如果指令没有缓存,则 cache_infoNone

在 3.4 版本加入。

在 3.11 版更改: 添加了字段 positions

在 3.13 版更改: 更改了字段 starts_line

添加了字段 start_offsetcache_offsetend_offsetbaseopnamebaseopcodejump_targetopargline_numbercache_info

class dis.Positions

如果信息不可用,某些字段可能为 None

lineno
end_lineno
col_offset
end_col_offset

在 3.11 版本中新增。

Python 编译器目前生成以下字节码指令。

通用指令

在下文中,我们将解释器堆栈称为 STACK,并像操作 Python 列表一样描述对其的操作。堆栈顶部对应于此语言中的 STACK[-1]

NOP

空操作码。由字节码优化器用作占位符,以及生成行跟踪事件。

NOT_TAKEN

空操作码。由解释器用于为 sys.monitoring 记录 BRANCH_LEFTBRANCH_RIGHT 事件。

在 3.14 版本加入。

POP_ITER

从堆栈顶部移除迭代器。

在 3.14 版本加入。

POP_TOP

移除栈顶项

STACK.pop()
END_FOR

移除栈顶项。等同于 POP_TOP。用于在循环结束时进行清理,因此得名。

3.12 新版功能.

END_SEND

实现 del STACK[-2]。用于在生成器退出时进行清理。

3.12 新版功能.

COPY(i)

将第 i 个项推送到堆栈顶部,而不从其原始位置移除

assert i > 0
STACK.append(STACK[-i])

在 3.11 版本中新增。

SWAP(i)

将堆栈顶部与第 i 个元素交换

STACK[-i], STACK[-1] = STACK[-1], STACK[-i]

在 3.11 版本中新增。

CACHE

与其说这是一个实际的指令,不如说这个操作码用于标记额外的空间,供解释器直接在字节码中缓存有用的数据。它被所有 dis 工具自动隐藏,但可以通过 show_caches=True 查看。

在逻辑上,这个空间是前一条指令的一部分。许多操作码期望后面跟着确切数量的缓存,并会指示解释器在运行时跳过它们。

已填充的缓存可能看起来像任意指令,因此在读取或修改包含已加速数据的原始、自适应字节码时应格外小心。

在 3.11 版本中新增。

一元操作

一元操作取栈顶元素,应用操作,然后将结果压回栈中。

UNARY_NEGATIVE

实现 STACK[-1] = -STACK[-1]

UNARY_NOT

实现 STACK[-1] = not STACK[-1]

在 3.13 版更改: 此指令现在需要一个确切的 bool 操作数。

UNARY_INVERT

实现 STACK[-1] = ~STACK[-1]

GET_ITER

实现 STACK[-1] = iter(STACK[-1])

GET_YIELD_FROM_ITER

如果 STACK[-1] 是一个 生成器迭代器协程 对象,它将保持原样。否则,实现 STACK[-1] = iter(STACK[-1])

在 3.5 版本加入。

TO_BOOL

实现 STACK[-1] = bool(STACK[-1])

在 3.13 版本加入。

二元和原地操作

二元操作从堆栈中移除顶部两个项(STACK[-1]STACK[-2])。它们执行操作,然后将结果放回堆栈。

原地操作类似于二元操作,但是当 STACK[-2] 支持时,操作是原地进行的,并且最终的 STACK[-1] 可能是(但不必须是)原始的 STACK[-2]

BINARY_OP(op)

实现二元和原地操作符(取决于 op 的值)

rhs = STACK.pop()
lhs = STACK.pop()
STACK.append(lhs op rhs)

在 3.11 版本中新增。

在 3.14 版更改: 当 oparg 为 NB_SUBSCR 时,实现二元下标操作(取代了 BINARY_SUBSCR 操作码)。

STORE_SUBSCR

实现

key = STACK.pop()
container = STACK.pop()
value = STACK.pop()
container[key] = value
DELETE_SUBSCR

实现

key = STACK.pop()
container = STACK.pop()
del container[key]
BINARY_SLICE

实现

end = STACK.pop()
start = STACK.pop()
container = STACK.pop()
STACK.append(container[start:end])

3.12 新版功能.

STORE_SLICE

实现

end = STACK.pop()
start = STACK.pop()
container = STACK.pop()
values = STACK.pop()
container[start:end] = value

3.12 新版功能.

协程操作码

GET_AWAITABLE(where)

实现 STACK[-1] = get_awaitable(STACK[-1]),其中 get_awaitable(o)o 是一个协程对象或带有 CO_ITERABLE_COROUTINE 旗标的生成器对象时会返回 o,或者解析 o.__await__

如果 where 操作数非零,它指示指令发生的位置:

  • 1: 在调用 __aenter__ 之后

  • 2: 在调用 __aexit__ 之后

在 3.5 版本加入。

在 3.11 版更改: 之前,此指令没有操作数参数。

GET_AITER

实现 STACK[-1] = STACK[-1].__aiter__()

在 3.5 版本加入。

在 3.7 版更改: 不再支持从 __aiter__ 返回可等待对象。

GET_ANEXT

STACK.append(get_awaitable(STACK[-1].__anext__())) 推入堆栈。有关 get_awaitable 的详细信息,请参见 GET_AWAITABLE

在 3.5 版本加入。

END_ASYNC_FOR

终止一个 async for 循环。处理在等待下一个项时引发的异常。堆栈在 STACK[-2] 中包含异步可迭代对象,在 STACK[-1] 中包含引发的异常。两者都会被弹出。如果异常不是 StopAsyncIteration,则会重新引发。

在 3.8 版本加入。

在 3.11 版更改: 堆栈上的异常表示现在由一个而非三个项组成。

CLEANUP_THROW

处理在通过当前帧调用 throw()close() 期间引发的异常。如果 STACK[-1]StopIteration 的实例,则从堆栈中弹出三个值并推送其 value 成员。否则,重新引发 STACK[-1]

3.12 新版功能.

杂项操作码

SET_ADD(i)

实现

item = STACK.pop()
set.add(STACK[-i], item)

用于实现集合推导式。

LIST_APPEND(i)

实现

item = STACK.pop()
list.append(STACK[-i], item)

用于实现列表推导式。

MAP_ADD(i)

实现

value = STACK.pop()
key = STACK.pop()
dict.__setitem__(STACK[-i], key, value)

用于实现字典推导式。

在 3.1 版本加入。

在 3.8 版更改: 映射的值是 STACK[-1],键是 STACK[-2]。以前,这两者是相反的。

对于所有 SET_ADD, LIST_APPENDMAP_ADD 指令,虽然添加的值或键/值对被弹出,但容器对象仍保留在堆栈上,以便在循环的后续迭代中可用。

RETURN_VALUE

STACK[-1] 返回给函数的调用者。

YIELD_VALUE

生成器 中产生 STACK.pop()

在 3.11 版更改: 操作数参数被设为栈深度。

在 3.12 版更改: 操作数参数被设为异常块深度,以高效地关闭生成器。

在 3.13 版更改: 如果此指令是 yield-from 或 await 的一部分,则 oparg 为 1,否则为 0

SETUP_ANNOTATIONS

检查 __annotations__ 是否在 locals() 中定义,如果未定义,则将其设置为空的 dict。仅当类或模块主体静态包含变量注解时,才会发出此操作码。

在 3.6 版本加入。

POP_EXCEPT

从堆栈中弹出一个值,用于恢复异常状态。

在 3.11 版更改: 堆栈上的异常表示现在由一个而非三个项组成。

RERAISE

重新引发当前在栈顶的异常。如果 oparg 非零,则从堆栈中弹出一个额外的值,用于设置当前帧的 f_lasti

在 3.9 版本中新增。

在 3.11 版更改: 堆栈上的异常表示现在由一个而非三个项组成。

PUSH_EXC_INFO

从堆栈中弹出一个值。将当前异常推到堆栈顶部。将最初弹出的值推回堆栈。用于异常处理程序中。

在 3.11 版本中新增。

CHECK_EXC_MATCH

except 执行异常匹配。测试 STACK[-2] 是否是与 STACK[-1] 匹配的异常。弹出 STACK[-1] 并推送测试的布尔结果。

在 3.11 版本中新增。

CHECK_EG_MATCH

except* 执行异常匹配。在表示 STACK[-2] 的异常组上应用 split(STACK[-1])

如果匹配,则从堆栈中弹出两个项,并推送不匹配的子组(如果完全匹配则为 None),后跟匹配的子组。如果不匹配,则弹出一个项(匹配类型)并推送 None

在 3.11 版本中新增。

WITH_EXCEPT_START

使用表示栈顶异常的参数 (type, val, tb) 调用堆栈中位置 4 的函数。用于在 with 语句中发生异常时,实现 context_manager.__exit__(*exc_info()) 调用。

在 3.9 版本中新增。

在 3.11 版更改: __exit__ 函数位于堆栈的第 4 位,而不是第 7 位。堆栈上的异常表示现在由一个而非三个项组成。

LOAD_COMMON_CONSTANT

将一个通用常量推入堆栈。解释器包含一个硬编码的此指令支持的常量列表。由 assert 语句用于加载 AssertionError

在 3.14 版本加入。

LOAD_BUILD_CLASS

builtins.__build_class__() 推入堆栈。稍后会调用它来构造一个类。

GET_LEN

执行 STACK.append(len(STACK[-1]))。用于需要与模式结构进行比较的 match 语句中。

在 3.10 版本加入。

MATCH_MAPPING

如果 STACK[-1]collections.abc.Mapping 的实例(或者,更技术性地说:如果它的 tp_flags 中设置了 Py_TPFLAGS_MAPPING 标志),则将 True 推入堆栈。否则,推送 False

在 3.10 版本加入。

MATCH_SEQUENCE

如果 STACK[-1]collections.abc.Sequence 的实例并且*不是* str/bytes/bytearray 的实例(或者,更技术性地说:如果它的 tp_flags 中设置了 Py_TPFLAGS_SEQUENCE 标志),则将 True 推入堆栈。否则,推送 False

在 3.10 版本加入。

MATCH_KEYS

STACK[-1] 是一个映射键的元组,而 STACK[-2] 是匹配的主体。如果 STACK[-2] 包含 STACK[-1] 中的所有键,则推送一个包含相应值的 tuple。否则,推送 None

在 3.10 版本加入。

在 3.11 版更改: 以前,此指令还会推送一个布尔值来表示成功(True)或失败(False)。

STORE_NAME(namei)

实现 name = STACK.pop()nameiname代码对象co_names 属性中的索引。编译器会尝试尽可能使用 STORE_FASTSTORE_GLOBAL

DELETE_NAME(namei)

实现 del name,其中 nameiname代码对象co_names 属性中的索引。

UNPACK_SEQUENCE(count)

STACK[-1] 解包为 count 个单独的值,这些值从右到左放入堆栈。要求值的数量恰好为 count

assert(len(STACK[-1]) == count)
STACK.extend(STACK.pop()[:-count-1:-1])
UNPACK_EX(counts)

实现带星号目标的赋值:将 STACK[-1] 中的可迭代对象解包为单独的值,其中值的总数可以小于可迭代对象中的项数:其中一个新值将是所有剩余项的列表。

列表值之前和之后的值的数量限制为 255。

列表值之前的值的数量编码在操作码的参数中。列表之后的值的数量(如果有)使用 EXTENDED_ARG 进行编码。因此,该参数可以看作一个两字节的值,其中 counts 的低字节是列表值之前的值的数量,counts 的高字节是列表之后的值的数量。

提取的值从右到左放入堆栈,即 a, *b, c = d 在执行后将存储为 STACK.extend((a, b, c))

STORE_ATTR(namei)

实现

obj = STACK.pop()
value = STACK.pop()
obj.name = value

其中 nameiname代码对象co_names 中的索引。

DELETE_ATTR(namei)

实现

obj = STACK.pop()
del obj.name

其中 nameiname代码对象co_names 中的索引。

STORE_GLOBAL(namei)

作用类似于 STORE_NAME,但将名称存储为全局变量。

DELETE_GLOBAL(namei)

作用类似于 DELETE_NAME,但删除一个全局名称。

LOAD_CONST(consti)

co_consts[consti] 推入堆栈。

LOAD_SMALL_INT(i)

将整数 i 推入堆栈。i 必须在 range(256) 范围内。

在 3.14 版本加入。

LOAD_NAME(namei)

将与 co_names[namei] 关联的值推入堆栈。该名称会先在局部变量中查找,然后在全局变量中查找,最后在内置变量中查找。

LOAD_LOCALS

将对局部变量字典的引用推入堆栈。这用于为 LOAD_FROM_DICT_OR_DEREFLOAD_FROM_DICT_OR_GLOBALS 准备命名空间字典。

3.12 新版功能.

LOAD_FROM_DICT_OR_GLOBALS(i)

从堆栈中弹出一个映射,并查找 co_names[namei] 的值。如果未找到该名称,则在全局变量中查找,然后在内置变量中查找,类似于 LOAD_GLOBAL。这用于在类主体内的 注解作用域 中加载全局变量。

3.12 新版功能.

BUILD_TEMPLATE

从一个字符串元组和一个插值元组构造一个新的 Template 实例,并将生成的对象推入堆栈

interpolations = STACK.pop()
strings = STACK.pop()
STACK.append(_build_template(strings, interpolations))

在 3.14 版本加入。

BUILD_INTERPOLATION(format)

从一个值及其源表达式构造一个新的 Interpolation 实例,并将生成的对象推入堆栈。

如果没有转换或格式说明符,format 将设置为 2

如果 format 的低位被设置,表示插值包含格式说明符。

如果 format >> 2 非零,表示插值包含转换。format >> 2 的值是转换类型(0 表示无转换,1 表示 !s2 表示 !r3 表示 !a)。

conversion = format >> 2
if format & 1:
    format_spec = STACK.pop()
else:
    format_spec = None
expression = STACK.pop()
value = STACK.pop()
STACK.append(_build_interpolation(value, expression, conversion, format_spec))

在 3.14 版本加入。

BUILD_TUPLE(count)

从栈中消费 count 个项来创建一个元组,并将结果元组推入栈中。

if count == 0:
    value = ()
else:
    value = tuple(STACK[-count:])
    STACK = STACK[:-count]

STACK.append(value)
BUILD_LIST(count)

工作方式类似 BUILD_TUPLE,但是创建一个列表。

BUILD_SET(count)

工作方式类似 BUILD_TUPLE,但是创建一个集合。

BUILD_MAP(count)

将一个新的字典对象推入栈中。弹出 2 * count 个项,使得该字典包含 count 个条目:{..., STACK[-4]: STACK[-3], STACK[-2]: STACK[-1]}

在 3.5 版本发生变更: 字典从栈中的项创建,而不是创建一个预设大小为 count 的空字典。

BUILD_STRING(count)

拼接栈中的 count 个字符串,并将结果字符串推入栈中。

在 3.6 版本加入。

LIST_EXTEND(i)

实现

seq = STACK.pop()
list.extend(STACK[-i], seq)

用于构建列表。

在 3.9 版本中新增。

SET_UPDATE(i)

实现

seq = STACK.pop()
set.update(STACK[-i], seq)

用于构建集合。

在 3.9 版本中新增。

DICT_UPDATE(i)

实现

map = STACK.pop()
dict.update(STACK[-i], map)

用于构建字典。

在 3.9 版本中新增。

DICT_MERGE(i)

类似于 DICT_UPDATE,但对于重复的键会引发异常。

在 3.9 版本中新增。

LOAD_ATTR(namei)

如果 namei 的最低位未设置,此指令会用 getattr(STACK[-1], co_names[namei>>1]) 替换 STACK[-1]

如果 namei 的最低位已设置,此指令将尝试从 STACK[-1] 对象加载名为 co_names[namei>>1] 的方法。STACK[-1] 会被弹出。此字节码区分两种情况:如果 STACK[-1] 具有正确名称的方法,字节码会推入未绑定方法和 STACK[-1]STACK[-1] 将被 CALLCALL_KW 用作调用未绑定方法时的第一个参数 (self)。否则,会将 NULL 和属性查找返回的对象推入栈中。

在 3.12 版本发生变更: 如果 namei 的最低位已设置,则会在属性或未绑定方法之前分别向栈中推入一个 NULLself

LOAD_SUPER_ATTR(namei)

此操作码实现了 super() 的两种形式:零参数和双参数(例如 super().method(), super().attrsuper(cls, self).method(), super(cls, self).attr)。

它从栈中弹出三个值(从栈顶向下):

  • self:当前方法的第一个参数

  • cls:定义当前方法的类

  • 全局的 super

就其参数而言,它的工作方式类似于 LOAD_ATTR,只是 namei 左移 2 位而不是 1 位。

namei 的最低位表示尝试加载方法,与 LOAD_ATTR 一样,这会导致将 NULL 和加载的方法推入栈中。当它未设置时,会将单个值推入栈中。

如果 namei 的次低位被设置,则表示这是一个对 super() 的双参数调用(未设置则表示零参数)。

3.12 新版功能.

COMPARE_OP(opname)

执行布尔运算。运算名称可以在 cmp_op[opname >> 5] 中找到。如果 opname 的第五个最低位被设置 (opname & 16),则结果应被强制转换为 bool

在 3.13 版本发生变更: oparg 的第五个最低位现在表示强制转换为 bool

IS_OP(invert)

执行 is 比较,如果 invert 为 1 则执行 is not

在 3.9 版本中新增。

CONTAINS_OP(invert)

执行 in 比较,如果 invert 为 1 则执行 not in

在 3.9 版本中新增。

IMPORT_NAME(namei)

导入模块 co_names[namei]STACK[-1]STACK[-2] 被弹出并提供 __import__()fromlistlevel 参数。模块对象被推入栈中。当前命名空间不受影响:对于一个正确的导入语句,后续的 STORE_FAST 指令会修改命名空间。

IMPORT_FROM(namei)

STACK[-1] 中的模块加载属性 co_names[namei]。结果对象被推入栈中,随后由 STORE_FAST 指令存储。

JUMP_FORWARD(delta)

字节码计数器增加 delta

JUMP_BACKWARD(delta)

字节码计数器减少 delta。检查中断。

在 3.11 版本中新增。

JUMP_BACKWARD_NO_INTERRUPT(delta)

字节码计数器减少 delta。不检查中断。

在 3.11 版本中新增。

POP_JUMP_IF_TRUE(delta)

如果 STACK[-1] 为真,字节码计数器增加 deltaSTACK[-1] 被弹出。

在 3.11 版本发生变更: oparg 现在是一个相对增量,而不是一个绝对目标。此操作码是一个伪指令,在最终字节码中被定向版本(前向/后向)替换。

在 3.12 版本发生变更: 这不再是伪指令。

在 3.13 版更改: 此指令现在需要一个确切的 bool 操作数。

POP_JUMP_IF_FALSE(delta)

如果 STACK[-1] 为假,字节码计数器增加 deltaSTACK[-1] 被弹出。

在 3.11 版本发生变更: oparg 现在是一个相对增量,而不是一个绝对目标。此操作码是一个伪指令,在最终字节码中被定向版本(前向/后向)替换。

在 3.12 版本发生变更: 这不再是伪指令。

在 3.13 版更改: 此指令现在需要一个确切的 bool 操作数。

POP_JUMP_IF_NOT_NONE(delta)

如果 STACK[-1] 不是 None,字节码计数器增加 deltaSTACK[-1] 被弹出。

在 3.11 版本中新增。

在 3.12 版本发生变更: 这不再是伪指令。

POP_JUMP_IF_NONE(delta)

如果 STACK[-1]None,字节码计数器增加 deltaSTACK[-1] 被弹出。

在 3.11 版本中新增。

在 3.12 版本发生变更: 这不再是伪指令。

FOR_ITER(delta)

STACK[-1] 是一个迭代器。调用其 __next__() 方法。如果这产生了一个新值,则将其推入栈中(迭代器保留在它下面)。如果迭代器表示已耗尽,则字节码计数器增加 delta

在 3.12 版本发生变更: 在 3.11 之前,当迭代器耗尽时会被弹出。

LOAD_GLOBAL(namei)

将名为 co_names[namei>>1] 的全局变量加载到栈上。

在 3.11 版本发生变更: 如果 namei 的最低位被设置,则在全局变量之前将一个 NULL 推入栈中。

LOAD_FAST(var_num)

将局部变量 co_varnames[var_num] 的引用推入栈中。

在 3.12 版本发生变更: 此操作码现在仅用于局部变量保证已初始化的情况。它不会引发 UnboundLocalError

LOAD_FAST_BORROW(var_num)

将局部变量 co_varnames[var_num] 的借用引用推入栈中。

在 3.14 版本加入。

LOAD_FAST_LOAD_FAST(var_nums)

co_varnames[var_nums >> 4]co_varnames[var_nums & 15] 的引用推入栈中。

在 3.13 版本加入。

LOAD_FAST_BORROW_LOAD_FAST_BORROW(var_nums)

co_varnames[var_nums >> 4]co_varnames[var_nums & 15] 的借用引用推入栈中。

在 3.14 版本加入。

LOAD_FAST_CHECK(var_num)

将局部变量 co_varnames[var_num] 的引用推入栈中,如果该局部变量尚未初始化,则引发 UnboundLocalError

3.12 新版功能.

LOAD_FAST_AND_CLEAR(var_num)

将局部变量 co_varnames[var_num] 的引用推入栈中(如果该局部变量尚未初始化,则推入 NULL),并将 co_varnames[var_num] 设置为 NULL

3.12 新版功能.

STORE_FAST(var_num)

STACK.pop() 存入局部变量 co_varnames[var_num]

STORE_FAST_STORE_FAST(var_nums)

STACK[-1] 存入 co_varnames[var_nums >> 4],将 STACK[-2] 存入 co_varnames[var_nums & 15]

在 3.13 版本加入。

STORE_FAST_LOAD_FAST(var_nums)

STACK.pop() 存入局部变量 co_varnames[var_nums >> 4],并将局部变量 co_varnames[var_nums & 15] 的引用推入栈中。

在 3.13 版本加入。

DELETE_FAST(var_num)

删除局部变量 co_varnames[var_num]

MAKE_CELL(i)

在槽位 i 创建一个新的单元。如果该槽位非空,则该值将被存入新的单元中。

在 3.11 版本中新增。

LOAD_DEREF(i)

加载“快速局部变量”存储区中槽位 i 的单元。将对该单元所含对象的引用推入栈中。

在 3.11 版本发生变更: i 不再通过 co_varnames 的长度进行偏移。

LOAD_FROM_DICT_OR_DEREF(i)

从栈中弹出一个映射,并在该映射中查找与“快速局部变量”存储区槽位 i 相关联的名称。如果在那里找不到该名称,则从槽位 i 中包含的单元加载它,类似于 LOAD_DEREF。这用于在类主体中(以前使用 LOAD_CLASSDEREF)和类主体内的 注解作用域 中加载 闭包变量

3.12 新版功能.

STORE_DEREF(i)

STACK.pop() 存入“快速局部变量”存储区中槽位 i 的单元中。

在 3.11 版本发生变更: i 不再通过 co_varnames 的长度进行偏移。

DELETE_DEREF(i)

清空“快速局部变量”存储区中槽位 i 的单元。由 del 语句使用。

在 3.2 版本加入。

在 3.11 版本发生变更: i 不再通过 co_varnames 的长度进行偏移。

COPY_FREE_VARS(n)

从闭包中将 n自由(闭包)变量 复制到帧中。在调用闭包时,无需在调用方一侧使用特殊代码。

在 3.11 版本中新增。

RAISE_VARARGS(argc)

根据 argc 的值,使用 raise 语句的三种形式之一引发异常:

  • 0: raise (重新引发前一个异常)

  • 1: raise STACK[-1] (引发在 STACK[-1] 的异常实例或类型)

  • 2: raise STACK[-2] from STACK[-1] (引发在 STACK[-2] 的异常实例或类型,并将 __cause__ 设置为 STACK[-1])

CALL(argc)

使用由 argc 指定的参数数量调用一个可调用对象。栈上的内容为(按升序排列):

  • 可调用对象

  • selfNULL

  • 其余的位置参数

argc 是位置参数的总数,不包括 self

CALL 从栈中弹出所有参数和可调用对象,用这些参数调用该可调用对象,并将可调用对象返回的返回值推入栈中。

在 3.11 版本中新增。

在 3.13 版本发生变更: 可调用对象现在总是在栈的相同位置出现。

在 3.13 版本发生变更: 带关键字参数的调用现在由 CALL_KW 处理。

CALL_KW(argc)

使用由 argc 指定的参数数量调用一个可调用对象,包括一个或多个命名参数。栈上的内容为(按升序排列):

  • 可调用对象

  • selfNULL

  • 其余的位置参数

  • 命名参数

  • 一个包含关键字参数名称的 tuple

argc 是位置参数和命名参数的总数,不包括 self。关键字参数名称元组的长度是命名参数的数量。

CALL_KW 从栈中弹出所有参数、关键字名称和可调用对象,用这些参数调用该可调用对象,并将可调用对象返回的返回值推入栈中。

在 3.13 版本加入。

CALL_FUNCTION_EX(flags)

使用可变数量的位置参数和关键字参数调用一个可调用对象。如果 flags 的最低位被设置,则栈顶包含一个映射对象,其中包含额外的关键字参数。在调用可调用对象之前,映射对象和可迭代对象分别被“解包”,其内容作为关键字参数和位置参数传入。CALL_FUNCTION_EX 从栈中弹出所有参数和可调用对象,用这些参数调用该可调用对象,并将可调用对象返回的返回值推入栈中。

在 3.6 版本加入。

PUSH_NULL

将一个 NULL 推入栈中。在调用序列中使用,以匹配非方法调用时由 LOAD_METHOD 推入的 NULL

在 3.11 版本中新增。

MAKE_FUNCTION

根据 STACK[-1] 处的代码对象构建一个新的函数对象,并将其推入栈中。

在 3.10 版本发生变更: 标志值 0x04 是一个字符串元组,而不是字典。

在 3.11 版本发生变更: 移除了 STACK[-1] 处的限定名称。

在 3.13 版本发生变更: 移除了栈上由 oparg 标志表示的额外函数属性。它们现在使用 SET_FUNCTION_ATTRIBUTE

SET_FUNCTION_ATTRIBUTE(flag)

在函数对象上设置一个属性。期望函数在 STACK[-1],要设置的属性值在 STACK[-2];消耗两者并在 STACK[-1] 留下函数。标志决定要设置哪个属性:

  • 0x01 一个包含仅限位置和位置或关键字参数的默认值的元组,按位置顺序排列

  • 0x02 一个包含仅关键字参数默认值的字典

  • 0x04 一个包含参数注解的字符串元组

  • 0x08 一个包含自由变量单元的元组,用于创建闭包

在 3.13 版本加入。

BUILD_SLICE(argc)

将一个切片对象推入栈中。argc 必须是 2 或 3。如果是 2,则实现

end = STACK.pop()
start = STACK.pop()
STACK.append(slice(start, end))

如果是 3,则实现

step = STACK.pop()
end = STACK.pop()
start = STACK.pop()
STACK.append(slice(start, end, step))

有关更多信息,请参阅内置函数 slice()

EXTENDED_ARG(ext)

为任何参数过大而无法放入默认一个字节的操作码添加前缀。ext 持有一个额外的字节,作为参数的高位。对于每个操作码,最多允许三个前缀 EXTENDED_ARG,形成一个从两字节到四字节的参数。

CONVERT_VALUE(oparg)

根据 oparg 将值转换为字符串

value = STACK.pop()
result = func(value)
STACK.append(result)
  • oparg == 1:在 value 上调用 str()

  • oparg == 2:在 value 上调用 repr()

  • oparg == 3:在 value 上调用 ascii()

用于实现格式化字符串字面量(f-strings)。

在 3.13 版本加入。

FORMAT_SIMPLE

格式化栈顶的值

value = STACK.pop()
result = value.__format__("")
STACK.append(result)

用于实现格式化字符串字面量(f-strings)。

在 3.13 版本加入。

FORMAT_WITH_SPEC

使用给定的格式说明符格式化给定的值

spec = STACK.pop()
value = STACK.pop()
result = value.__format__(spec)
STACK.append(result)

用于实现格式化字符串字面量(f-strings)。

在 3.13 版本加入。

MATCH_CLASS(count)

STACK[-1] 是一个关键字属性名称的元组,STACK[-2] 是被匹配的类,STACK[-3] 是匹配的主体。count 是位置子模式的数量。

弹出 STACK[-1], STACK[-2]STACK[-3]。如果 STACK[-3]STACK[-2] 的实例,并且具有 countSTACK[-1] 所要求的位置和关键字属性,则推入一个包含提取属性的元组。否则,推入 None

在 3.10 版本加入。

在 3.11 版更改: 以前,此指令还会推送一个布尔值来表示成功(True)或失败(False)。

RESUME(context)

一个空操作。执行内部跟踪、调试和优化检查。

context 操作数由两部分组成。最低两位表示 RESUME 发生的位置:

  • 0 函数的开始,该函数既不是生成器、协程,也不是异步生成器

  • 1yield 表达式之后

  • 2yield from 表达式之后

  • 3await 表达式之后

如果 RESUME 处于异常深度 1,则下一位为 1,否则为 0

在 3.11 版本中新增。

在 3.13 版本发生变更: oparg 的值已更改,以包含有关异常深度的信息。

RETURN_GENERATOR

从当前帧创建一个生成器、协程或异步生成器。用作上述可调用对象的代码对象中的第一个操作码。清除当前帧并返回新创建的生成器。

在 3.11 版本中新增。

SEND(delta)

等价于 STACK[-1] = STACK[-2].send(STACK[-1])。用于 yield fromawait 语句。

如果调用引发 StopIteration,则从栈中弹出顶部值,推入异常的 value 属性,并将字节码计数器增加 delta

在 3.11 版本中新增。

HAVE_ARGUMENT

这实际上不是一个操作码。它标识了在 [0,255] 范围内的操作码中,不使用其参数的操作码和使用其参数的操作码(分别为 < HAVE_ARGUMENT>= HAVE_ARGUMENT)之间的分界线。

如果你的应用程序使用伪指令或专门指令,请改用 hasarg 集合。

在 3.6 版本发生变更: 现在每个指令都有一个参数,但 < HAVE_ARGUMENT 的操作码会忽略它。在此之前,只有 >= HAVE_ARGUMENT 的操作码有参数。

在 3.12 版本发生变更: 伪指令已添加到 dis 模块中,对于它们来说,与 HAVE_ARGUMENT 比较并不能表明它们是否使用其参数。

自 3.13 版本起弃用: 请改用 hasarg

CALL_INTRINSIC_1

调用一个带有一个参数的内置函数。将 STACK[-1] 作为参数传递,并将 STACK[-1] 设置为结果。用于实现非性能关键的功能。

操作数决定调用哪个内置函数:

操作数

描述

INTRINSIC_1_INVALID

无效

INTRINSIC_PRINT

将参数打印到标准输出。在 REPL 中使用。

INTRINSIC_IMPORT_STAR

为指定模块执行 import *

INTRINSIC_STOPITERATION_ERROR

StopIteration 异常中提取返回值。

INTRINSIC_ASYNC_GEN_WRAP

包装一个异步生成器值

INTRINSIC_UNARY_POSITIVE

执行一元 + 运算

INTRINSIC_LIST_TO_TUPLE

将列表转换为元组

INTRINSIC_TYPEVAR

创建一个 typing.TypeVar

INTRINSIC_PARAMSPEC

创建一个 typing.ParamSpec

INTRINSIC_TYPEVARTUPLE

创建一个 typing.TypeVarTuple

INTRINSIC_SUBSCRIPT_GENERIC

返回用参数下标化的 typing.Generic

INTRINSIC_TYPEALIAS

创建一个 typing.TypeAliasType;用于 type 语句。参数是一个包含类型别名名称、类型参数和值的元组。

3.12 新版功能.

CALL_INTRINSIC_2

调用一个带有两个参数的内置函数。用于实现非性能关键的功能:

arg2 = STACK.pop()
arg1 = STACK.pop()
result = intrinsic2(arg1, arg2)
STACK.append(result)

操作数决定调用哪个内置函数:

操作数

描述

INTRINSIC_2_INVALID

无效

INTRINSIC_PREP_RERAISE_STAR

计算从 try-except* 中要引发的 ExceptionGroup

INTRINSIC_TYPEVAR_WITH_BOUND

创建一个带有边界的 typing.TypeVar

INTRINSIC_TYPEVAR_WITH_CONSTRAINTS

创建一个带有约束的 typing.TypeVar

INTRINSIC_SET_FUNCTION_TYPE_PARAMS

设置函数的 __type_params__ 属性。

3.12 新版功能.

LOAD_SPECIAL

STACK[-1] 上执行特殊方法查找。如果 type(STACK[-1]).__xxx__ 是一个方法,则将 type(STACK[-1]).__xxx__; STACK[-1] 留在栈上。如果 type(STACK[-1]).__xxx__ 不是一个方法,则将 STACK[-1].__xxx__; NULL 留在栈上。

在 3.14 版本加入。

伪指令

这些操作码不会出现在 Python 字节码中。它们被编译器使用,但在生成字节码之前被替换为真正的操作码或被移除。

SETUP_FINALLY(target)

为接下来的代码块设置一个异常处理程序。如果发生异常,值栈的层级将恢复到当前状态,并且控制权将转移到 target 处的异常处理程序。

SETUP_CLEANUP(target)

类似于 SETUP_FINALLY,但在发生异常时还会将最后一条指令 (lasti) 推入栈中,以便 RERAISE 可以恢复它。如果发生异常,值栈的层级和帧上的最后一条指令将恢复到当前状态,并且控制权将转移到 target 处的异常处理程序。

SETUP_WITH(target)

类似于 SETUP_CLEANUP,但在发生异常时,在将控制权转移到 target 处的异常处理程序之前,会从栈中再弹出一个项。

这个变体用于 withasync with 结构中,它们会将上下文管理器的 __enter__()__aenter__() 的返回值推入栈中。

POP_BLOCK

标记与最后一个 SETUP_FINALLYSETUP_CLEANUPSETUP_WITH 相关联的代码块的结束。

LOAD_CONST_IMMORTAL(consti)

工作方式类似 LOAD_CONST,但对于不朽对象更高效。

JUMP
JUMP_NO_INTERRUPT

非定向的相对跳转指令,由汇编器替换为其定向(前向/后向)的对应指令。

JUMP_IF_TRUE
JUMP_IF_FALSE

不影响栈的条件跳转。被替换为序列 COPY 1TO_BOOLPOP_JUMP_IF_TRUE/FALSE

LOAD_CLOSURE(i)

将“快速局部变量”存储区中槽位 i 所含单元的引用推入栈中。

请注意,LOAD_CLOSURE 在汇编器中被替换为 LOAD_FAST

在 3.13 版本发生变更: 此操作码现在是一个伪指令。

操作码集合

提供这些集合是为了对字节码指令进行自动内省:

在 3.12 版本发生变更: 这些集合现在也包含伪指令和被插桩的指令。这些是值 >= MIN_PSEUDO_OPCODE>= MIN_INSTRUMENTED_OPCODE 的操作码。

dis.opname

操作名称序列,可使用字节码进行索引。

dis.opmap

将操作名称映射到字节码的字典。

dis.cmp_op

所有比较操作名称的序列。

dis.hasarg

使用其参数的字节码序列。

3.12 新版功能.

dis.hasconst

访问常量的字节码序列。

dis.hasfree

访问自由(闭包)变量的字节码序列。此处的 'free' 指的是当前作用域中被内部作用域引用的名称,或外部作用域中被此作用域引用的名称。它 包括对全局或内置作用域的引用。

dis.hasname

通过名称访问属性的字节码序列。

dis.hasjump

具有跳转目标的字节码序列。所有跳转都是相对的。

在 3.13 版本加入。

dis.haslocal

访问局部变量的字节码序列。

dis.hascompare

布尔运算的字节码序列。

dis.hasexc

设置异常处理程序的字节码序列。

3.12 新版功能.

dis.hasjrel

具有相对跳转目标的字节码序列。

自 3.13 版本起弃用: 现在所有跳转都是相对的。请使用 hasjump

dis.hasjabs

具有绝对跳转目标的字节码序列。

自 3.13 版本起弃用: 现在所有跳转都是相对的。此列表为空。