dis — Python 字节码反汇编器

源代码: Lib/dis.py


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

CPython 实现细节: 字节码是 CPython 解释器的实现细节。不保证字节码在 Python 版本之间不会被添加、删除或更改。本模块的使用不应被认为可以在不同的 Python 虚拟机或 Python 版本之间工作。

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

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

在 3.11 版本中更改: 一些指令会伴随一个或多个内联缓存条目,这些条目采用 CACHE 指令的形式。这些指令默认是隐藏的,但是可以通过传递 show_caches=True 到任何 dis 实用程序来显示。此外,解释器现在会调整字节码以针对不同的运行时条件进行专门化。可以通过传递 adaptive=True 来显示自适应字节码。

在 3.12 版本中更改: 跳转的参数是目标指令相对于跳转指令的 CACHE 条目之后立即出现的指令的偏移量。

因此,CACHE 指令的存在对于前向跳转是透明的,但在推理后向跳转时需要考虑。

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

例如:给定函数 myfunc()

def myfunc(alist):
    return len(alist)

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

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

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

(“2” 是行号)。

命令行接口

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

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

接受以下选项

-h, --help

显示用法并退出。

-C, --show-caches

显示内联缓存。

-O, --show-offsets

显示指令的偏移量。

如果指定了 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)

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

这是对下面列出的许多函数的便捷包装,最值得注意的是 get_instructions(),因为迭代 Bytecode 实例会生成字节码操作作为 Instruction 实例。

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

如果 current_offset 不是 None,则它指的是反汇编代码中的指令偏移量。设置此项意味着 dis() 将在指定的 opcode 上显示“当前指令”标记。

如果 show_cachesTrue,则 dis() 将显示解释器用于专门化字节码的内联缓存条目。

如果 adaptiveTrue,则 dis() 将显示可能与原始字节码不同的专门化字节码。

如果 show_offsetsTrue,则 dis() 将在输出中包含指令偏移量。

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 参数。

示例

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

分析函数

dis 模块还定义了以下分析函数,它们直接将输入转换为所需的输出。如果只执行一个操作,那么中间的分析对象就没什么用处,这些函数就很有用。

dis.code_info(x)

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

请注意,代码信息字符串的确切内容高度依赖于实现,并且它们可能会在 Python VM 或 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)

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

反汇编以文本形式写入提供的 file 参数(如果提供),否则写入 sys.stdout

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

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

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

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

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

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

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

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

反汇编回溯的堆栈顶层函数,如果没有传递回溯,则使用最后一个回溯。 指示导致异常的指令。

反汇编以文本形式写入提供的 file 参数(如果提供),否则写入 sys.stdout

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

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

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

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

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

  1. 行号,对于每行的第一条指令

  2. 当前指令,指示为 -->

  3. 标记的指令,指示为 >>

  4. 指令的地址,

  5. 操作码名称,

  6. 操作参数,以及

  7. 括号中参数的解释。

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

反汇编以文本形式写入提供的 file 参数(如果提供),否则写入 sys.stdout

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

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

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

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)

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

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

在 3.10 版本中更改: 使用 PEP 626 co_lines() 方法,而不是 co_firstlinenoco_lnotab 属性的 代码对象

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

dis.findlabels(code)

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

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

计算具有参数 opargopcode 的堆栈效应。

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

3.4 版本新增。

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

在 3.13 版本中更改: 如果省略 oparg (或 None),现在将返回 oparg=0 的堆栈效应。之前,对于使用其参数的操作码,这是一个错误。当 opcode 不使用 oparg 时,传递整数 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 对象。

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

无操作代码。用作字节码优化器的占位符,并生成行跟踪事件。

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 版本中添加。

BINARY_SUBSCR

实现

key = STACK.pop()
container = STACK.pop()
STACK.append(container[key])
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]),其中如果 o 是协程对象或具有 CO_ITERABLE_COROUTINE 标志的生成器对象,则 get_awaitable(o) 返回 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 版本中添加。

BEFORE_ASYNC_WITH

STACK[-1] 解析 __aenter____aexit__。将 __aexit____aenter__() 的结果推送到堆栈

STACK.extend((__aexit__, __aenter__())

在 3.5 版本中添加。

杂项操作码

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_ADDLIST_APPENDMAP_ADD 指令,在添加的值或键/值对被弹出时,容器对象仍然保留在堆栈上,以便在循环的进一步迭代中可用。

RETURN_VALUE

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

RETURN_CONST(consti)

co_consts[consti] 返回给函数的调用者。

在 3.12 版本中添加。

YIELD_VALUE

生成器 中产生 STACK.pop()

在 3.11 版本中更改: oparg 设置为堆栈深度。

在 3.12 版本中更改: oparg 设置为异常块深度,以便高效关闭生成器。

在 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

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

在 3.9 版本中加入。

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

LOAD_ASSERTION_ERROR

AssertionError 推送到堆栈上。由 assert 语句使用。

在 3.9 版本中加入。

LOAD_BUILD_CLASS

builtins.__build_class__() 推送到堆栈上。稍后将调用它来构造一个类。

BEFORE_WITH

此操作码在 with 代码块开始之前执行多个操作。首先,它从上下文管理器中加载 __exit__(),并将其推送到堆栈上,供 WITH_EXCEPT_START 稍后使用。然后,调用 __enter__()。最后,将调用 __enter__() 方法的结果推送到堆栈上。

在 3.11 版本中添加。

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()nameico_names 属性中 name 的索引,该属性是代码对象。编译器会尝试尽可能使用 STORE_FASTSTORE_GLOBAL

DELETE_NAME(namei)

实现 del name,其中 nameico_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

其中 nameico_names 中名称的索引,此 代码对象 的属性。

DELETE_ATTR(namei)

实现

obj = STACK.pop()
del obj.name

其中 nameico_names 中名称的索引,此 代码对象 的属性。

STORE_GLOBAL(namei)

STORE_NAME 的工作方式相同,但将名称存储为全局名称。

DELETE_GLOBAL(namei)

DELETE_NAME 的工作方式相同,但删除全局名称。

LOAD_CONST(consti)

co_consts[consti] 压入堆栈。

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_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_CONST_KEY_MAP(count)

BUILD_MAP 的专门用于常量键的版本。弹出堆栈顶部的元素,该元素包含一个键的元组,然后从 STACK[-2] 开始,弹出 count 个值以形成构建的字典中的值。

在 3.6 版本中加入。

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 的低位未设置,则此操作会将 STACK[-1] 替换为 getattr(STACK[-1], co_names[namei>>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 版本中变更:现在,操作数的第五低位表示强制转换为 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__() 的 *fromlist* 和 *level* 参数。模块对象被压入堆栈。当前命名空间不受影响:对于正确的导入语句,后续的 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] 为 true,则将字节码计数器增加 *delta*。STACK[-1] 被弹出。

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

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

在 3.13 版本中更改: 此指令现在要求一个精确的 bool 操作数。

POP_JUMP_IF_FALSE(delta)

如果 STACK[-1] 为 false,则将字节码计数器增加 *delta*。STACK[-1] 被弹出。

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

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

在 3.13 版本中更改: 此指令现在要求一个精确的 bool 操作数。

POP_JUMP_IF_NOT_NONE(delta)

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

此操作码是一个伪指令,在最终字节码中被定向版本(向前/向后)替换。

在 3.11 版本中添加。

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

POP_JUMP_IF_NONE(delta)

如果 STACK[-1]None,则将字节码计数器增加 *delta*。STACK[-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_LOAD_FAST(var_nums)

将对 co_varnames[var_nums >> 4]co_varnames[var_nums & 15] 的引用推入堆栈。

在 3.13 版本中添加。

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 语句的 3 种形式之一引发异常

  • 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 版本中更改: 删除了堆栈上的额外函数属性,这些属性由操作码标志发出信号。它们现在使用 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-字符串)。

在 3.13 版本中添加。

FORMAT_SIMPLE

格式化堆栈顶部的值

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

用于实现格式化的文字字符串(f-字符串)。

在 3.13 版本中添加。

FORMAT_WITH_SPEC

使用给定的格式规范格式化给定值

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

用于实现格式化的文字字符串(f-字符串)。

在 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 处于 except-depth 1,则下一位为 1,否则为 0

在 3.11 版本中添加。

在 3.13 版本中更改: oparg 值已更改,以包含有关 except-depth 的信息

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 版本中添加。

伪指令

这些操作码不会出现在 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 关联的代码块的结束。

JUMP
JUMP_NO_INTERRUPT

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

LOAD_CLOSURE(i)

将对“快速局部变量”存储中槽 i 中包含的单元格的引用压入堆栈。

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

在 3.13 版本中更改: 此操作码现在是一个伪指令。

LOAD_METHOD

优化的未绑定方法查找。作为设置了参数标志的 LOAD_ATTR 操作码发出。

操作码集合

这些集合用于自动内省字节码指令

在 3.12 版本中更改: 这些集合现在也包含伪指令和检测指令。这些是值 >= MIN_PSEUDO_OPCODE>= MIN_INSTRUMENTED_OPCODE 的操作码。

dis.opname

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

dis.opmap

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

dis.cmp_op

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

dis.hasarg

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

在 3.12 版本中添加。

dis.hasconst

访问常量的字节码序列。

dis.hasfree

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

dis.hasname

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

dis.hasjump

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

在 3.13 版本中添加。

dis.haslocal

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

dis.hascompare

布尔运算的字节码序列。

dis.hasexc

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

在 3.12 版本中添加。

dis.hasjrel

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

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

dis.hasjabs

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

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