dis — Python 字节码反汇编器

源代码: Lib/dis.py


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

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

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

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

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

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

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

示例: 给定函数 myfunc()

def myfunc(alist):
    return len(alist)

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

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

  3           2 LOAD_GLOBAL              1 (NULL + len)
             12 LOAD_FAST                0 (alist)
             14 CALL                     1
             22 RETURN_VALUE

(“2” 是行号)。

命令行界面

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

python -m dis [-h] [infile]

接受以下选项

-h, --help

显示用法并退出。

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

字节码分析

在版本 3.4 中添加。

字节码分析 API 允许将 Python 代码片段包装在 Bytecode 对象中,该对象提供对已编译代码的详细信息的轻松访问。

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

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

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

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

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

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

如果 adaptiveTrue,则 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

分析函数

The 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

除非 depthNone,否则递归的最大深度受 depth 限制。 depth=0 表示不递归。

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

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

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

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

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

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

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

反汇编回溯的栈顶函数,如果未传递任何回溯,则使用最后一个回溯。导致异常的指令将被指示。

反汇编结果将以文本形式写入提供的 file 参数,如果没有提供,则写入 sys.stdout

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

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

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

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

  1. 每行的第一条指令的行号

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

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

  4. 指令的地址

  5. 操作码名称

  6. 操作参数

  7. 参数的解释(在括号中)。

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

反汇编结果将以文本形式写入提供的 file 参数,如果没有提供,则写入 sys.stdout

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

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

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

返回提供的函数、方法、源代码字符串或代码对象中的指令的迭代器。

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

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

show_cachesadaptive 参数的工作方式与 dis() 中的相同。

在版本 3.4 中添加。

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

dis.findlinestarts(code)

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

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

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

dis.findlabels(code)

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

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

计算 opcode 带参数 oparg 的堆栈效应。

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

在版本 3.4 中添加。

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

Python 字节码指令

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

class dis.Instruction

字节码操作的详细信息

opcode

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

opname

操作的人类可读名称

arg

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

argval

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

argrepr

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

offset

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

starts_line

此操作码开始的行(如果有),否则为 None

is_jump_target

True 如果其他代码跳转到这里,否则为 False

positions

dis.Positions 对象,保存此指令覆盖的开始和结束位置。

在版本 3.4 中添加。

在 3.11 版本中变更: 添加了字段 positions

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; 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]

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

二元运算和就地运算

二元运算从堆栈中移除最上面的两个元素 (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]),其中 get_awaitable(o) 返回 o 如果 o 是一个协程对象或带有 CO_ITERABLE_COROUTINE 标志的生成器对象,或者解析 o.__await__

如果 where 操作数不为零,则表示指令出现的位置

  • 1:在调用 __aenter__ 之后

  • 2:在调用 __aexit__ 之后

在版本 3.5 中添加。

在版本 3.11 中更改: 以前,此指令没有 oparg。

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 设置为异常块深度,用于高效地关闭生成器。

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_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]))

在 3.10 版本中添加。

MATCH_MAPPING

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

在 3.10 版本中添加。

MATCH_SEQUENCE

如果 STACK[-1]collections.abc.Sequence 的实例,并且不是 str/bytes/bytearray 的实例(或者更准确地说:如果它在 Py_TPFLAGS_SEQUENCE 标志在其 tp_flags 中设置),则将 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()nameinameco_names 属性中的索引,该属性属于 代码对象。编译器尝试使用 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 个项目创建一个元组,并将生成的元组推入堆栈。

assert count > 0
STACK, values = STACK[:-count], STACK[-count:]
STACK.append(tuple(values))
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 的低位没有设置,则用 getattr(STACK[-1], co_names[namei>>1]) 替换 STACK[-1]

如果 namei 的最低位被设置,则尝试从 STACK[-1] 对象中加载名为 co_names[namei>>1] 的方法。 STACK[-1] 被弹出。此字节码区分两种情况:如果 STACK[-1] 具有与正确名称匹配的方法,则字节码将推入未绑定方法和 STACK[-1]STACK[-1] 将在调用未绑定方法时被用作第一个参数 (self) CALL。否则,将推入 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] 中找到。

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 中更改: 这不再是伪指令。

POP_JUMP_IF_FALSE(delta)

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

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

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

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_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] 中。

DELETE_FAST(var_num)

删除本地变量 co_varnames[var_num]

MAKE_CELL(i)

在插槽 i 中创建一个新的单元格。如果该插槽不为空,则将该值存储到新的单元格中。

在 3.11 版本中添加。

LOAD_CLOSURE(i)

将“快速局部变量”存储区中插槽 i 中包含的单元格的引用压入堆栈。变量的名称是 co_fastlocalnames[i]

请注意,LOAD_CLOSURE 实际上是 LOAD_FAST 的别名。它存在是为了使字节码更易读。

在版本 3.11 中更改: i 不再以 co_varnames 的长度为偏移量。

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 指定的参数数量调用可调用对象,包括由前面的 KW_NAMES 指定的命名参数(如果有)。堆栈中(按升序)包含以下内容之一:

  • NULL

  • 可调用对象

  • 位置参数

  • 命名参数

  • 可调用对象

  • self

  • 剩余的位置参数

  • 命名参数

argc 是位置参数和命名参数的总数,不包括当 NULL 不存在时 self

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

在 3.11 版本中添加。

CALL_FUNCTION_EX(flags)

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

在 3.6 版本中添加。

PUSH_NULL

NULL 压入堆栈。在调用序列中使用,以匹配 LOAD_METHOD 为非方法调用压入的 NULL

在 3.11 版本中添加。

KW_NAMES(consti)

CALL 添加前缀。将对 co_consts[consti] 的引用存储到内部变量中,供 CALL 使用。 co_consts[consti] 必须是一个字符串元组。

在 3.11 版本中添加。

MAKE_FUNCTION(flags)

在堆栈上压入一个新的函数对象。从下到上,消耗的堆栈必须包含值,如果参数带有指定的标志值

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

  • 0x02 关键字参数默认值的字典

  • 0x04 包含参数注释的字符串元组

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

  • 与函数关联的代码(位于 STACK[-1]

在 3.10 版本中变更: 标志值 0x04 是字符串元组而不是字典

在 3.11 版本中变更: STACK[-1] 中的限定名称已删除。

BUILD_SLICE(argc)

在堆栈上压入一个切片对象。argc 必须是 2 或 3。如果它是 2,则实现

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

如果它是 3,则实现

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

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

EXTENDED_ARG(ext)

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

FORMAT_VALUE(flags)

用于实现格式化字面量字符串(f-字符串)。从堆栈中弹出可选的 fmt_spec,然后弹出必需的 valueflags 的解释如下

  • (flags & 0x03) == 0x00: value 按原样格式化。

  • (flags & 0x03) == 0x01: 在格式化之前,对 value 调用 str()

  • (flags & 0x03) == 0x02: 在格式化之前,对 value 调用 repr()

  • (flags & 0x03) == 0x03: 在格式化之前,对 value 调用 ascii()

  • (flags & 0x04) == 0x04: 从堆栈中弹出 fmt_spec 并使用它,否则使用空 fmt_spec

格式化使用 PyObject_Format() 执行。结果被压入堆栈。

在 3.6 版本中添加。

MATCH_CLASS(count)

STACK[-1] 是一个关键字属性名称元组,STACK[-2] 是要匹配的类,STACK[-3] 是匹配主题。

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

在 3.10 版本中添加。

在 3.11 版本中变更: 以前,此指令还推入一个布尔值,表示成功(True)或失败(False)。

RESUME(where)

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

where 操作数标记 RESUME 发生的位置

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

  • 1yield 表达式之后

  • 2yield from 表达式之后

  • 3await 表达式之后

在 3.11 版本中添加。

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 的比较并不表示它们是否使用其参数。

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.push(result)

操作数决定调用哪个内联函数

操作数

描述

INTRINSIC_2_INVALID

无效

INTRINSIC_PREP_RERAISE_STAR

计算 ExceptionGrouptry-except* 抛出。

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_METHOD

优化的未绑定方法查找。作为 LOAD_ATTR 操作码发出,并在参数中设置一个标志。

操作码集合

这些集合用于自动检查字节码指令

Changed in version 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.hasjrel

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

dis.hasjabs

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

dis.haslocal

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

dis.hascompare

布尔运算的字节码序列。

dis.hasexc

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

在 3.12 版本中添加。