# 32.12. `dis` - Python字节码的反汇编程序

源代码： Lib/dis.py

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

CPython实现细节：字节码是CPython解释器的实现细节。不保证字节码不会在Python版本之间添加，删除或更改。使用此模块不应被视为在Python VM或Python版本上工作。

示例：给定函数`myfunc()`：

    def myfunc(alist):
        return len(alist)

以下命令可用于显示`myfunc()`的反汇编：

    >>> dis.dis(myfunc)
      2           0 LOAD_GLOBAL              0 (len)
                  3 LOAD_FAST                0 (alist)
                  6 CALL_FUNCTION            1
                  9 RETURN_VALUE

（“2”是行号）。

## 32.12.1. 字节码分析

版本3.4中的新功能。

字节码分析API允许将Python代码片段封装在`Bytecode`对象中，从而可以轻松访问已编译代码的详细信息。

class `dis.``Bytecode`(x, *, first_line=None, current_offset=None)

  分析对应于函数，生成器，方法，源代码字符串或代码对象（由`compile()`返回）的字节码。

  这是围绕下面列出的许多函数的一个便捷包装，最着名的是`get_instructions()`，因为对`Bytecode`实例的迭代产生的字节码操作与`Instruction`

  如果first_line不是None，它表示应该为反汇编代码中的第一个源行报告的行号。否则，源行信息（如果有的话）直接取自已拆解的代码对象。

  如果current_offset不为None，则它指的是在反汇编代码中的指令偏移量。设置这意味着`dis()`将针对指定的操作码显示“当前指令”标记。

  classmethod `from_traceback`(tb)

    从给定traceback构造`Bytecode`实例，将current_offset设置为负责异常的指令。

  `codeobj`

    编译的代码对象。

  `first_line`

    代码对象的第一个源代码行（如果可用）

  `dis`()

    返回字节码操作的格式化视图（与`dis.dis()`打印的相同，但返回为多行字符串）。

  `info`()

    返回格式化的多行字符串，其中包含有关代码对象的详细信息，例如`code_info()`。

例：

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

## 32.12.2. 分析函数

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

`dis.``code_info`(x)

  返回带有提供的函数，生成器，方法，源代码字符串或代码对象的详细代码对象信息的格式化多行字符串。

  注意，代码信息字符串的确切内容高度依赖于实现，并且它们可以在Python VM或Python版本中任意改变。

  版本3.2中的新功能。

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

  将文件（或`sys.stdout`）中提供的函数，方法，源代码字符串或代码对象的详细代码对象信息打印到文件指定）。

  这是`打印（code_info（x）， file = file）`的方便缩写，用于在解释器提示符处进行交互式探索。

  版本3.2中的新功能。

  在版本3.4中更改：添加了文件参数。

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

  拆分x对象。x可以表示模块，类，方法，函数，生成器，代码对象，源代码字符串或原始字节码的字节序列。对于模块，它会拆散所有函数。对于一个类，它反汇编所有的方法（包括类和静态方法）。对于代码对象或原始字节码序列，每个字节码指令打印一行。在反汇编之前，字符串首先用`compile()`内建函数编译为代码对象。如果没有提供对象，则此函数将反汇编最后一个回溯。

  反汇编作为文本写入提供的文件参数（如果提供）和`sys.stdout`，否则。

  在版本3.4中更改：添加了文件参数。

`dis.``distb`(tb=None, *, file=None)

  如果没有通过，则使用最后一个traceback来反汇编回栈顶层函数。指示引起异常的指令。

  反汇编作为文本写入提供的文件参数（如果提供）和`sys.stdout`，否则。

  在版本3.4中更改：添加了文件参数。

`dis.``disassemble`(code, lasti=-1, *, file=None)
`dis.``disco`(code, lasti=-1, *, file=None)

  拆分代码对象，如果提供lasti，则指示最后一条指令。输出分为以下列：

  1. 行号，用于每行的第一条指令
  2. 指示为`-->`的当前指令，
  3. 由`>>`指示的标记指令，
  4. 指令的地址，
  5. 操作代码名称，
  6. 操作参数和
  7. 括号中的参数的解释。

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

  反汇编作为文本写入提供的文件参数（如果提供）和`sys.stdout`，否则。

  在版本3.4中更改：添加了文件参数。

`dis.``get_instructions`(x, *, first_line=None)

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

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

  如果first_line不是None，它表示应该为反汇编代码中的第一个源行报告的行号。否则，源行信息（如果有的话）直接取自已拆解的代码对象。

  版本3.4中的新功能。

`dis.``findlinestarts`(code)

  这个生成器函数使用代码对象代码的`co_firstlineno`和`co_lnotab`属性来找到源代码中行开头的偏移。它们生成为`（偏移， lineno）`对。

`dis.``findlabels`(code)

  检测作为跳转目标的代码对象代码中的所有偏移量，并返回这些偏移量的列表。

`dis.``stack_effect`(opcode[, oparg])

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

  版本3.4中的新功能。

## 32.12.3. Python字节码指令

`get_instructions()`函数和`Bytecode`类提供了字节码指令作为`Instruction`实例的详细信息：

class `dis.``Instruction`

  字节码操作的详细信息

  `opcode`

    操作的数字代码，对应于下面列出的操作码值和Opcode collections中的字节码值。

  `opname`

    操作的人类可读名称

  `arg`

    操作的数字参数（如果有），否则为无

  `argval`

    解析的arg值（如果已知），否则与arg相同

  `argrepr`

    操作参数的可读描述

  `offset`

    在字节码序列内的操作的开始索引

  `starts_line`

    由此操作码开始的行（如果有），否则为无

  `is_jump_target`

    `True`如果其他代码跳到此处，否则`False`

  版本3.4中的新功能。

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

一般指令

`NOP`

  不做任何代码。由字节码优化器用作占位符。

`POP_TOP`

  删除顶部（TOS）项。

`ROT_TWO`

  交换两个最顶层的堆栈项。

`ROT_THREE`

  将第二和第三堆叠物品提升一个位置，将顶部向下移动到位置三。

`DUP_TOP`

  复制堆栈顶部的引用。

`DUP_TOP_TWO`

  复制堆栈顶部的两个引用，使它们按相同的顺序。

一元操作

一元操作占用堆栈的顶部，应用操作，并将结果推回堆栈。

`UNARY_POSITIVE`

  实施`TOS = + TOS`。

`UNARY_NEGATIVE`

  实施`TOS = -TOS`。

`UNARY_NOT`

  实施`TOS = 而不是 TOS`。

`UNARY_INVERT`

  实现`TOS = 〜TOS`。

`GET_ITER`

  实现`TOS = iter（TOS）`。

`GET_YIELD_FROM_ITER`

  如果`TOS`是generator iterator或coroutine对象，则保持原样。否则，实现`TOS = iter（TOS）`。

  版本3.5中的新功能。

二进制操作

二进制操作从堆栈中删除堆栈顶部（TOS）和第二个最顶层堆栈项目（TOS1）。他们执行操作，并将结果放回堆栈。

`BINARY_POWER`

  实施`TOS = TOS1 ** TOS`

`BINARY_MULTIPLY`

  实施`TOS = TOS1 * TOS`

`BINARY_MATRIX_MULTIPLY`

  实施`TOS = TOS1 @ TOS`

  版本3.5中的新功能。

`BINARY_FLOOR_DIVIDE`

  实现`TOS = TOS1 // TOS`

`BINARY_TRUE_DIVIDE`

  实施`TOS = TOS1 / TOS`

`BINARY_MODULO`

  实施`TOS = TOS1 ％ TOS`

`BINARY_ADD`

  实施`TOS = TOS1 + TOS`

`BINARY_SUBTRACT`

  实施`TOS = TOS1 - TOS`

`BINARY_SUBSCR`

  实现`TOS = TOS1 [TOS]`。

`BINARY_LSHIFT`

  实施`TOS = TOS1 TOS t0 >。`

`BINARY_RSHIFT`

  实施`TOS = TOS1 >＆gt； TOS t0 >。`

`BINARY_AND`

  实施`TOS = TOS1 ＆amp； TOS`

`BINARY_XOR`

  实施`TOS = TOS1 ^ TOS`

`BINARY_OR`

  实施`TOS = TOS1 | TOS`

就地操作

就地操作类似于二进制操作，因为它们删除TOS和TOS1，并将结果推回堆栈，但操作是在TOS1支持它时就地完成的，并且生成的TOS可以是（但不是是原来的TOS1。

`INPLACE_POWER`

  实施`TOS = TOS1 ** TOS t0>。`

`INPLACE_MULTIPLY`

  立即实施`TOS = TOS1 * TOS t0 >。`

`INPLACE_MATRIX_MULTIPLY`

  实施`TOS = TOS1 @ TOS t0 >。`

  版本3.5中的新功能。

`INPLACE_FLOOR_DIVIDE`

  实施`TOS = TOS1 // TOS t0>。`

`INPLACE_TRUE_DIVIDE`

  实施`TOS = TOS1 / TOS t0 >。`

`INPLACE_MODULO`

  实施`TOS = TOS1 ％ TOS t0 >。`

`INPLACE_ADD`

  立即实施`TOS = TOS1 + TOS t0 >。`

`INPLACE_SUBTRACT`

  实施`TOS = TOS1 - TOS t0 >。`

`INPLACE_LSHIFT`

  实现`TOS = TOS1 TOS` 。

`INPLACE_RSHIFT`

  Implements in-place `TOS = TOS1 >> TOS`.

`INPLACE_AND`

  实施`TOS = TOS1 ＆amp； TOS t0>。`

`INPLACE_XOR`

  立即实施`TOS = TOS1 ^ TOS t0 >。`

`INPLACE_OR`

  实施`TOS = TOS1 | TOS t0 >。`

`STORE_SUBSCR`

  实现`TOS1[TOS] = TOS2`.

`DELETE_SUBSCR`

  实现`del TOS1[TOS]`.

协程操作码

`GET_AWAITABLE`

  Implements `TOS = get_awaitable(TOS)`, where `get_awaitable(o)` returns `o` if `o` is a coroutine object or a generator object with the CO_ITERABLE_COROUTINE flag, or resolves `o.__await__`.

`GET_AITER`

  实现`TOS = get_awaitable（TOS .__ aiter __()）`。有关`get_awaitable`的详细信息，请参阅`GET_AWAITABLE`

`GET_ANEXT`

  实现`PUSH(get_awaitable(TOS.__anext__()))`。有关`get_awaitable`的详细信息，请参阅`GET_AWAITABLE`

`BEFORE_ASYNC_WITH`

  从堆栈顶部的对象解析`__aenter__`和`__aexit__`。将`__aexit__`和`__aenter__()`的结果推送到堆栈。

`SETUP_ASYNC_WITH`

  创建一个新的框架对象。

其他操作码

`PRINT_EXPR`

  实现交互式模式的表达式语句。TOS从堆栈中删除并打印。在非交互模式下，使用`POP_TOP`终止表达式语句。

`BREAK_LOOP`

  由于`break`语句而终止循环。

`CONTINUE_LOOP`(target)

  由于`continue`语句，继续循环。target是要跳转的地址（应为`FOR_ITER`指令）。

`SET_ADD`(i)

  调用`set.add（TOS1 [-i]， TOS）`。用于实现集推式。

`LIST_APPEND`(i)

  调用`list.append（TOS [-i]， TOS）`。用于实现列表推导式。

`MAP_ADD`(i)

  调用`dict.setitem（TOS1 [-i]， TOS， TOS1）`。用于实现dict推导式。

对于所有`SET_ADD`，`LIST_APPEND`和`MAP_ADD`指令，当添加的值或键/值对被弹出时，容器对象保持开启使得它可用于循环的进一步迭代。

`RETURN_VALUE`

  返回带有TOS的函数的调用者。

`YIELD_VALUE`

  弹出TOS并从generator生成它。

`YIELD_FROM`

  弹出TOS并从generator中委托它作为辅助参数。

  版本3.3中的新功能。

`IMPORT_STAR T0>`

  将所有不以`'_'`开头的符号直接从模块TOS加载到本地命名空间。加载所有名称后，将弹出该模块。此操作码从 模块 导入 *实现`。`

`POP_BLOCK`

  从块堆栈中删除一个块。每帧，有一堆块，表示嵌套循环，try语句等。

`POP_EXCEPT`

  从块堆栈中删除一个块。弹出的块必须是异常处理程序块，这是在输入except处理程序时隐式创建的。除了从帧堆栈中弹出无关的值之外，最后三个弹出的值还用于恢复异常状态。

`END_FINALLY`

  终止`finally`子句。解释器调用异常是否必须重新提升，或者该函数是否返回，并继续下一个下一个块。

`LOAD_BUILD_CLASS`

  将`builtins.__build_class__()`推送到堆栈。它稍后由`CALL_FUNCTION`调用以构造一个类。

`SETUP_WITH`(delta)

  这个操作码在块开始之前执行几个操作。首先，它从上下文管理器加载`__exit__()`并将其推送到堆栈以供稍后由`WITH_CLEANUP`使用。然后，调用`__enter__()`，并按下指向delta的finally块。最后，调用enter方法的结果被推入堆栈。The next opcode will either ignore it (`POP_TOP`), or store it in (a) variable(s) (`STORE_FAST`, `STORE_NAME`, or `UNPACK_SEQUENCE`).

`WITH_CLEANUP_START`

  当`with`语句块退出时清理堆栈。TOS是上下文管理器的`__exit__()`绑定方法。TOS下面有1-3个值，表示如何/为什么输入finally子句：

  - SECOND = `None`
  - （SECOND，THIRD）=（`WHY_{RETURN,CONTINUE}`），retval
  - SECOND = `WHY_*`；没有retval下面
  - （SECOND，THIRD，FOURTH）= exc_info()

  在最后一种情况下，调用`TOS（SECOND， THIRD， FOURTH）`，否则`TOS（无， 无， 无）`。将SECOND和调用的结果推送到堆栈。

`WITH_CLEANUP_FINISH`

  从堆栈中弹出“exit”函数调用的异常类型和结果。

  如果堆栈表示异常，和函数调用返回“true”值，此信息为“zapped”，并替换为单个`WHY_SILENCED`，以防止`END_FINALLY`重新提高异常。（但非本地gotos将仍然恢复。）

所有以下操作码都需要参数。参数是两个字节，最高有效字节。

`STORE_NAME`(namei)

  实施`名称 = TOS`。namei是代码对象的属性`co_names`中名称的索引。如果可能，编译器尝试使用`STORE_FAST`或`STORE_GLOBAL`。

`DELETE_NAME`(namei)

  实现`del 名称`，其中namei是`co_names`目的。

`UNPACK_SEQUENCE`(count)

  将TOS解包到计数个别值中，这些值从右到左放在堆栈中。

`UNPACK_EX`(counts)

  使用已加星标的目标实现分配：将TOS中的迭代包解压为单个值，其中值的总数可以小于可迭代项中的项数：其中一个新值将是所有剩余项的列表。

  计数的低字节是列表值之前的值的数量，的高字节计数它之后的值的数量。结果值从右到左放在堆栈中。

`STORE_ATTR`(namei)

  实现`TOS.name = TOS1`，其中namei `co_names`。

`DELETE_ATTR`(namei)

  使用namei作为`co_names`中的索引，实现`del TOS.name`

`STORE_GLOBAL`(namei)

  用作`STORE_NAME`，但将名称存储为全局名称。

`DELETE_GLOBAL`(namei)

  用作`DELETE_NAME`，但会删除全局名称。

`LOAD_CONST`(consti)

  将`co_consts[consti]`推送到堆栈。

`LOAD_NAME`(namei)

  将与`co_names[namei]`关联的值推送到堆栈。

`BUILD_TUPLE`(count)

  从堆栈中创建消耗计数的元组，并将生成的元组推送到堆栈。

`BUILD_LIST`(count)

  用作`BUILD_TUPLE`，但会创建列表。

`BUILD_SET`(count)

  用作`BUILD_TUPLE`，但会创建一个集合。

`BUILD_MAP`(count)

  将一个新的字典对象推送到堆栈。字典预先设置为保存计数条目。

`LOAD_ATTR`(namei)

  用`getattr（TOS， co_names [namei]）`替换TOS。

`COMPARE_OP`(opname)

  执行布尔操作。操作名称可以在`cmp_op[opname]`中找到。

`IMPORT_NAME`(namei)

  导入模块`co_names[namei]`。TOS和TOS1被弹出并提供`__import__()`的从列表和级参数。模块对象被推入堆栈。当前命名空间不受影响：对于正确的导入语句，后续的`STORE_FAST`指令修改命名空间。

`IMPORT_FROM`(namei)

  从TOS中找到的模块加载属性`co_names[namei]`。结果对象被推入堆栈，随后通过`STORE_FAST`指令存储。

`JUMP_FORWARD`(delta)

  通过delta增加字节码计数器。

`POP_JUMP_IF_TRUE`(target)

  如果TOS为真，则将字节码计数器设置为target。TOS弹出。

`POP_JUMP_IF_FALSE`(target)

  如果TOS为假，则将字节码计数器设置为target。TOS弹出。

`JUMP_IF_TRUE_OR_POP`(target)

  如果TOS为真，则将字节码计数器设置为target，并在堆栈上保留TOS。否则（TOS为假），TOS被弹出。

`JUMP_IF_FALSE_OR_POP`(target)

  如果TOS为假，则将字节码计数器设置为target，并在堆栈上保留TOS。否则（TOS为真），TOS被弹出。

`JUMP_ABSOLUTE`(target)

  将字节码计数器设置为目标。

`FOR_ITER`(delta)

  TOS是iterator。调用其`__next__()`方法。如果这产生一个新的值，推它在堆栈（留下下面的迭代器）。如果迭代器指示它被耗尽，则TOS被弹出，并且字节代码计数器递增delta。

`LOAD_GLOBAL`(namei)

  将名为`co_names[namei]`的全局加载到堆栈中。

`SETUP_LOOP`(delta)

  将一个循环的块推送到块堆栈。该块跨越当前指令，大小为delta字节。

`SETUP_EXCEPT`(delta)

  将try块从try-except子句推送到块堆栈。delta指向第一个除外块。

`SETUP_FINALLY`(delta)

  将try块从try-except子句推送到块堆栈上。delta指向finally块。

`LOAD_FAST`(var_num)

  将对本地`co_varnames[var_num]`的引用推送到堆栈。

`STORE_FAST`(var_num)

  将TOS存储到本地`co_varnames[var_num]`中。

`DELETE_FAST`(var_num)

  删除本地`co_varnames[var_num]`。

`LOAD_CLOSURE`(i)

  推动对单元格的插槽i中包含的单元格的引用和自由变量存储。变量的名称为`co_cellvars[i]` if i小于co_cellvars的长度。否则为`co_freevars [i - len（co_cellvars）]`。

`LOAD_DEREF`(i)

  加载单元格的插槽i中包含的单元格和自由变量存储。推动对单元格在堆栈上包含的对象的引用。

`LOAD_CLASSDEREF`(i)

  很像`LOAD_DEREF`，但在咨询单元格之前首先检查本地字典。这用于在类体中加载自由变量。

`STORE_DEREF`(i)

  将TOS存储到单元的插槽i中包含的单元和自由变量存储器中。

`DELETE_DEREF`(i)

  清空单元格的插槽i中包含的单元格和自由变量存储。由`del`语句使用。

`RAISE_VARARGS`(argc)

  引发异常。argc指示引数语句的参数数，范围为0到3。处理程序将找到跟踪作为TOS2，参数为TOS1，异常为TOS。

`CALL_FUNCTION`(argc)

  调用函数。argc的低字节表示位置参数的数量，高字节表示关键字参数的数量。在堆栈上，操作码首先找到关键字参数。对于每个关键字参数，值是在键的顶部。在关键字参数下面，位置参数在堆栈上，最右边的参数在顶部。在参数下面，要调用的函数对象在堆栈上。弹出所有函数参数，函数本身关闭堆栈，并推送返回值。

`MAKE_FUNCTION`(argc)

  在堆栈上推送一个新的函数对象。从底部到顶部，消耗的堆栈必须包含

  - `argc ＆amp； 0xFF`
  - `（argc > 8） ＆amp； 0xFF / t0>名称和默认参数对，其名称正好位于堆栈上的对象下方，用于仅限关键字的参数`
  - `（argc ＆gt； 16） ＆amp； 0x7FFF / t0>参数注释对象`
  - 一个列出注解的参数名称的元组（仅当存在ony注释对象时）
  - 与功能相关的代码（在TOS1处）
  - 函数的qualified name（在TOS处）

`MAKE_CLOSURE`(argc)

  创建一个新的函数对象，设置其__ closure __槽，并将其推入堆栈。TOS是函数的qualified name，TOS1是与函数相关联的代码，TOS2是包含闭包自由变量的单元格的元组。argc解释为`MAKE_FUNCTION`；注释和默认值也在TOS2之下的相同顺序。

`BUILD_SLICE`(argc)

  在堆栈上推动一个切片对象。argc必须为2或3。如果为2，则按下`切片（TOS1， TOS） 如果是3，则按下`切片（TOS2， TOS1， TOS）`` 有关详细信息，请参阅`slice()`内建函数。

`EXTENDED_ARG`(ext)

  前缀任何操作码，其参数太大，不适合默认的两个字节。ext包含两个附加字节，与后续操作码的参数一起构成一个四字节参数，ext是两个最高有效字节。

`CALL_FUNCTION_VAR`(argc)

  调用函数。argc解释为`CALL_FUNCTION`。堆栈上的顶层元素包含变量参数列表，后跟关键字和位置参数。

`CALL_FUNCTION_KW`(argc)

  调用函数。argc解释为`CALL_FUNCTION`。堆栈顶部的元素包含关键字参数字典，其次是显式关键字和位置参数。

`CALL_FUNCTION_VAR_KW`(argc)

  调用函数。argc解释为`CALL_FUNCTION`。栈上的顶层元素包含关键字arguments字典，后面跟着variable-arguments tuple，后面是显式关键字和位置参数。

`HAVE_ARGUMENT T0>`

  这不是一个真正的操作码。它标识不接受参数的操作码之间的分界线`HAVE_ARGUMENT`和执行`> = HAVE_ARGUMENT`。

## 32.12.4. 操作码集合

这些容器用于自动内省字节码指令：

`dis.``opname`

  操作名称序列，使用字节码可索引。

`dis.``opmap`

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

`dis.``cmp_op`

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

`dis.``hasconst`

  具有常量参数的字节码序列。

`dis.``hasfree`

  访问自由变量的字节码序列（请注意，在本上下文中的“自由”是指内部作用域引用的当前作用域中的名称或从此作用域引用的外部作用域中的名称。它不包括对全局或内置作用域的引用）。

`dis.``hasname`

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

`dis.``hasjrel`

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

`dis.``hasjabs`

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

`dis.``haslocal`

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

`dis.``hascompare`

  布尔运算的字节码序列。