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.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_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_caches 为
True
,dis()
将显示解释器用来特化字节码的内联缓存条目。如果 adaptive 为
True
,dis()
将显示可能与原始字节码不同的特化字节码。如果 show_offsets 为
True
,dis()
将在输出中包含指令偏移量。如果 show_positions 为
True
,dis()
将在输出中包含指令的源代码位置。- classmethod from_traceback(tb, *, show_caches=False)¶
根据给定的回溯信息构造一个
Bytecode
实例,并将 current_offset 设置为导致异常的指令。
- codeobj¶
已编译的代码对象。
- first_line¶
代码对象的第一个源代码行号(如果可用)。
- info()¶
返回一个格式化的多行字符串,其中包含有关代码对象的详细信息,类似于
code_info()
。
在 3.7 版更改: 现在可以处理协程和异步生成器对象。
在 3.11 版更改: 添加了 show_caches 和 adaptive 参数。
在 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 限制,除非它为
None
。depth=0
表示不递归。如果 show_caches 为
True
,此函数将显示解释器用于特化字节码的内联缓存条目。如果 adaptive 为
True
,此函数将显示可能与原始字节码不同的特化字节码。在 3.4 版更改: 添加了 file 参数。
在 3.7 版更改: 实现了递归反汇编并添加了 depth 参数。
在 3.7 版更改: 现在可以处理协程和异步生成器对象。
在 3.11 版更改: 添加了 show_caches 和 adaptive 参数。
在 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_caches 和 adaptive 参数。
在 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,则指示最后一条指令。输出分为以下几列:
指令的源代码位置。如果 show_positions 为 true,则显示完整的位置信息。否则(默认)只显示行号。
当前指令,用
-->
表示,一个带标签的指令,用
>>
表示,指令的地址,
操作码名称,
操作参数,以及
括号中对参数的解释。
参数解释能识别局部和全局变量名、常量值、分支目标和比较运算符。
如果提供了 file 参数,反汇编结果将作为文本写入该文件,否则写入
sys.stdout
。在 3.4 版更改: 添加了 file 参数。
在 3.11 版更改: 添加了 show_caches 和 adaptive 参数。
在 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_caches 和 adaptive 参数。
在 3.13 版更改: show_caches 参数已弃用且不起作用。迭代器会生成填充了 cache_info 字段的
Instruction
实例(无论 show_caches 的值如何),并且不再为缓存条目生成单独的项。
- dis.findlinestarts(code)¶
这个生成器函数使用 代码对象 code 的
co_lines()
方法来查找源代码中行开始的偏移量。它们以(offset, lineno)
对的形式生成。在 3.6 版更改: 行号可以递减。以前,它们总是递增的。
在 3.10 版更改: 使用了 PEP 626 的
co_lines()
方法,取代了 代码对象 的co_firstlineno
和co_lnotab
属性。在 3.13 版更改: 对于不映射到源代码行的字节码,行号可以为
None
。
- dis.findlabels(code)¶
检测原始编译字节码字符串 code 中所有作为跳转目标的偏移量,并返回这些偏移量的列表。
- dis.stack_effect(opcode, oparg=None, *, jump=None)¶
计算带参数 oparg 的 opcode 的堆栈效应。
如果代码有跳转目标且 jump 为
True
,stack_effect()
将返回跳转的堆栈效应。如果 jump 为False
,它将返回不跳转的堆栈效应。如果 jump 为None
(默认值),它将返回两种情况下的最大堆栈效应。在 3.4 版本加入。
在 3.8 版更改: 添加了 jump 参数。
在 3.13 版更改: 如果省略
oparg
(或为None
),现在将返回oparg=0
的堆栈效应。以前,对于使用参数的操作码,这会引发错误。当opcode
不使用参数时,传递一个整数oparg
也不再是错误;在这种情况下,oparg
会被忽略。
Python 字节码指令¶
get_instructions()
函数和 Bytecode
类以 Instruction
实例的形式提供字节码指令的详细信息。
- class dis.Instruction¶
字节码操作的详细信息
- opname¶
人类可读的操作名称
- arg¶
操作的数字参数(如果有),否则为
None
- argval¶
解析后的参数值(如果有),否则为
None
- argrepr¶
操作参数的人类可读描述(如果有),否则为空字符串。
- 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)
的三元组,其中name
和size
描述缓存格式,data
是缓存的内容。如果指令没有缓存,则cache_info
为None
。
在 3.4 版本加入。
在 3.11 版更改: 添加了字段
positions
。在 3.13 版更改: 更改了字段
starts_line
。添加了字段
start_offset
、cache_offset
、end_offset
、baseopname
、baseopcode
、jump_target
、oparg
、line_number
和cache_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_LEFT
和BRANCH_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_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_APPEND
和 MAP_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__()
推入堆栈。稍后会调用它来构造一个类。
- 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()
。namei 是 name 在代码对象的co_names
属性中的索引。编译器会尝试尽可能使用STORE_FAST
或STORE_GLOBAL
。
- 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
- 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_DEREF
和LOAD_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
表示!s
,2
表示!r
,3
表示!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]
将被CALL
或CALL_KW
用作调用未绑定方法时的第一个参数 (self
)。否则,会将NULL
和属性查找返回的对象推入栈中。在 3.12 版本发生变更: 如果
namei
的最低位已设置,则会在属性或未绑定方法之前分别向栈中推入一个NULL
或self
。
- LOAD_SUPER_ATTR(namei)¶
此操作码实现了
super()
的两种形式:零参数和双参数(例如super().method()
,super().attr
和super(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__()
的 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]
为真,字节码计数器增加 delta。STACK[-1]
被弹出。在 3.11 版本发生变更: oparg 现在是一个相对增量,而不是一个绝对目标。此操作码是一个伪指令,在最终字节码中被定向版本(前向/后向)替换。
在 3.12 版本发生变更: 这不再是伪指令。
在 3.13 版更改: 此指令现在需要一个确切的
bool
操作数。
- POP_JUMP_IF_FALSE(delta)¶
如果
STACK[-1]
为假,字节码计数器增加 delta。STACK[-1]
被弹出。在 3.11 版本发生变更: oparg 现在是一个相对增量,而不是一个绝对目标。此操作码是一个伪指令,在最终字节码中被定向版本(前向/后向)替换。
在 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_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
的长度进行偏移。
- 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
指定的参数数量调用一个可调用对象。栈上的内容为(按升序排列):可调用对象
self
或NULL
其余的位置参数
argc
是位置参数的总数,不包括self
。CALL
从栈中弹出所有参数和可调用对象,用这些参数调用该可调用对象,并将可调用对象返回的返回值推入栈中。在 3.11 版本中新增。
在 3.13 版本发生变更: 可调用对象现在总是在栈的相同位置出现。
在 3.13 版本发生变更: 带关键字参数的调用现在由
CALL_KW
处理。
- CALL_KW(argc)¶
使用由
argc
指定的参数数量调用一个可调用对象,包括一个或多个命名参数。栈上的内容为(按升序排列):可调用对象
self
或NULL
其余的位置参数
命名参数
一个包含关键字参数名称的
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)
用于实现格式化字符串字面量(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]
的实例,并且具有 count 和STACK[-1]
所要求的位置和关键字属性,则推入一个包含提取属性的元组。否则,推入None
。在 3.10 版本加入。
在 3.11 版更改: 以前,此指令还会推送一个布尔值来表示成功(
True
)或失败(False
)。
- RESUME(context)¶
一个空操作。执行内部跟踪、调试和优化检查。
context
操作数由两部分组成。最低两位表示RESUME
发生的位置:0
函数的开始,该函数既不是生成器、协程,也不是异步生成器1
在yield
表达式之后2
在yield from
表达式之后3
在await
表达式之后
如果 RESUME 处于异常深度
1
,则下一位为1
,否则为0
。在 3.11 版本中新增。
在 3.13 版本发生变更: oparg 的值已更改,以包含有关异常深度的信息。
- RETURN_GENERATOR¶
从当前帧创建一个生成器、协程或异步生成器。用作上述可调用对象的代码对象中的第一个操作码。清除当前帧并返回新创建的生成器。
在 3.11 版本中新增。
- SEND(delta)¶
等价于
STACK[-1] = STACK[-2].send(STACK[-1])
。用于yield from
和await
语句。如果调用引发
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
处的异常处理程序之前,会从栈中再弹出一个项。这个变体用于
with
和async with
结构中,它们会将上下文管理器的__enter__()
或__aenter__()
的返回值推入栈中。
- POP_BLOCK¶
标记与最后一个
SETUP_FINALLY
、SETUP_CLEANUP
或SETUP_WITH
相关联的代码块的结束。
- LOAD_CONST_IMMORTAL(consti)¶
工作方式类似
LOAD_CONST
,但对于不朽对象更高效。
- 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.hasname¶
通过名称访问属性的字节码序列。
- dis.hasjump¶
具有跳转目标的字节码序列。所有跳转都是相对的。
在 3.13 版本加入。
- dis.haslocal¶
访问局部变量的字节码序列。
- dis.hascompare¶
布尔运算的字节码序列。
- dis.hasexc¶
设置异常处理程序的字节码序列。
3.12 新版功能.
- dis.hasjabs¶
具有绝对跳转目标的字节码序列。
自 3.13 版本起弃用: 现在所有跳转都是相对的。此列表为空。