dis --- Python bytecode的反組譯器

原始碼:Lib/dis.py


dis 模組支援反組譯分析 CPython bytecode。CPython bytecode 作為輸入的模組被定義於 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" 是行号)。

命令行接口

dis 模块可以在命令行下作为一个脚本来发起调用:

python -m dis [-h] [infile]

可以接受以下选项:

-h, --help

显示用法并退出。

如果指定了 infile,其反汇编代码将被写入到标准输出。 否则,反汇编将在从标准输入接收的已编译源代码上进行。

字节码分析

在 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() 将针对指定的操作码显示“当前指令”标记。

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

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

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 參數。

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)

这个生成器函数使用 代码对象 codeco_lines() 方法来查找源代码中行开头的偏移量。 它们将作为 (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)

使用参数 oparg 计算 opcode 的堆栈效果。

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

在 3.4 版新加入.

在 3.8 版的變更: 新增 jump 參數。

Python字节码说明

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

class dis.Instruction

字节码操作的详细信息

opcode

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

opname

人类可读的操作名称

arg

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

argval

已解析的 arg 值(如果有的话),否则为 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] 是一个 generator iteratorcoroutine 对象则它将保持原样。 否则,将实现 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 是一个有 CO_ITERABLE_COROUTINE 旗标的协程对象或生成器对象时,返回 o,否则解析 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

generator 产生 STACK.pop()

在 3.11 版的變更: oparg 被设为堆栈深度。

在 3.12 版的變更: oparg 被设为异常块的深度,以确保关闭生成器的效率。

SETUP_ANNOTATIONS

检查 __annotations__ 是否在 locals() 中定义,如果没有,它被设置为空 dict 。只有在类或模块体静态地包含 variable annotations 时才会发出此操作码。

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

在 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,其中 namei代码对象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 编码。 因此参数可以被认为是一个双字节值,其中低位字节代表列表前的值的数量,高位字节代表其后的值的数量。

提取出来的值被自右向左放入栈中,也就是说 a, *b, c = d 在执行完成之后会被这样储存: STACK.extend((a, b, c))

STORE_ATTR(namei)

实现:

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

其中 namei 是 name 在 代码对象co_names 中的索引。

DELETE_ATTR(namei)

实现:

obj = STACK.pop()
del obj.name

其中 namei 是 name 在 代码对象co_names 中的索引。

STORE_GLOBAL(namei)

类似于 STORE_NAME 但会将 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 的低位未设置,则将 STACK[-1] 替换为 getattr(STACK[-1], co_names[namei>>1])

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

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] 以提供 fromlistlevel 参数给 __import__() 。 模块对象会被推入栈顶。 当前命名空间不受影响:对于一条标准 import 语句,会执行后续的 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 版的變更: 操作符的参数现在是一个相对的差值而不是一个绝对的目标量。 此操作码是一个伪指令,在最终的字节码里被定向的版本( forward/backward )取代。

在 3.12 版的變更: 该操作码现在不再是伪指令。

POP_JUMP_IF_FALSE(delta)

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

在 3.11 版的變更: 操作符的参数现在是一个相对的差值而不是一个绝对的目标量。 此操作码是一个伪指令,在最终的字节码里被定向的版本( forward/backward )取代。

在 3.12 版的變更: 该操作码现在不再是伪指令。

POP_JUMP_IF_NOT_NONE(delta)

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

此操作码是一个伪指令,在最终的字节码里被定向的版本( forward/backward )取代。

在 3.11 版新加入.

在 3.12 版的變更: 该操作码现在不再是伪指令。

POP_JUMP_IF_NONE(delta)

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

此操作码是一个伪指令,在最终的字节码里被定向的版本( forward/backward )取代。

在 3.11 版新加入.

在 3.12 版的變更: 该操作码现在不再是伪指令。

FOR_ITER(delta)

STACK[-1] 是一个 iterator。 调用其 __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)

推入一个指向包含在 "fast locals" 存储的 i 号槽位的单元的引用。 变量名为 co_fastlocalnames[i]

注意 LOAD_CLOSURE 实际上是 LOAD_FAST 的一个别名。 它的存在是为了让字节码的可读性更好一些。

在 3.11 版的變更: i 不再是长度为 co_varnames 的偏移量。

LOAD_DEREF(i)

加载包含在 "fast locals" 存储的 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() 存放到 "fast locals" 存储中包含在 i 号槽位的单元内。

在 3.11 版的變更: i 不再是 co_varnames 的长度的偏移量。

DELETE_DEREF(i)

清空 "fast locals" 存储中包含在 i 号槽位的单元。 被用于 del 语句。

在 3.2 版新加入.

在 3.11 版的變更: i 不再是 co_varnames 的长度的偏移量。

COPY_FREE_VARS(n)

n 个自由变量从闭包拷贝到帧中。 当调用闭包时不再需要调用方添加特殊的代码。

在 3.11 版新加入.

RAISE_VARARGS(argc)

使用 raise 语句的 3 种形式之一引发异常,具体形式取决于 argc 的值:

  • 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] 是匹配的目标主题。 count 是位置子模式的数量。

弹出 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

计算 ExceptionGroup 以从 try-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

非定向相对跳转指令会被汇编器转换为它们定向版本( forward/backward )

LOAD_METHOD

经优化的非绑定方法查找。 以在 arg 中设置了旗标的 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

访问了 free 变量的字节码的序列。 这里的 ‘free’ 指的是当前作用域中被内层作用域引用的名称或外层作用域被当前作用域引用的名称。 它 不包括 全局或内置作用域的引用。

dis.hasname

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

dis.hasjrel

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

dis.hasjabs

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

dis.haslocal

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

dis.hascompare

布尔运算的字节码序列。

dis.hasexc

设置一个异常处理句柄的字节码序列。

在 3.12 版新加入.