为什么反汇编 python 类定义显示两个相同的 LOAD_CONST 和类名?

Jor*_*ado 4 python class disassembly python-3.x

我想知道为什么在LOAD_CONST定义一个类时,python 会两次使用相同的值(类名)。当我运行此代码时:

from dis import dis

dis("class A(): pass")
Run Code Online (Sandbox Code Playgroud)

这是我得到的输出:

  1           0 LOAD_BUILD_CLASS
              2 LOAD_CONST               0 (<code object A at 0x0000021DCE681B70, file "<dis>", line 1>)
              4 LOAD_CONST               1 ('A')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               1 ('A')
             10 CALL_FUNCTION            2
             12 STORE_NAME               0 (A)
             14 LOAD_CONST               2 (None)
             16 RETURN_VALUE

Disassembly of <code object A at 0x0000021DCE681B70, file "<dis>", line 1>:
  1           0 LOAD_NAME                0 (__name__)
              2 STORE_NAME               1 (__module__)
              4 LOAD_CONST               0 ('A')
              6 STORE_NAME               2 (__qualname__)
              8 LOAD_CONST               1 (None)
             10 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

正如您在第 3 行和第 5 行中看到的,有两个LOAD_CONST类名。

LOAD_CONST如果已经加载了类名,为什么还要使用相同的数据呢?这与它们MAKE_FUNCTION之间有什么关系LOAD_CONST吗?

我在 python 3.7.4 64 位上运行这个

wim*_*wim 7

TL;DR: CPython 中的类型创建临时使用一个函数对象作为类体。第一个“A”用于此函数的名称。第二个“A”用于类名。

这篇文章的其余部分详细解释了这个反汇编:

 0 LOAD_BUILD_CLASS
Run Code Online (Sandbox Code Playgroud)

builtins.__build_class__入堆栈。稍后由 CALL_FUNCTION 调用以构造一个类。

 2 LOAD_CONST               0 (<code object A at 0xCAFEF00D, file "<dis>", line 1>)
Run Code Online (Sandbox Code Playgroud)

将代码 obj 压入堆栈(这实际上包含已解析的类块 - 继续阅读)

 4 LOAD_CONST               1 ('A')
Run Code Online (Sandbox Code Playgroud)

将“A”压入堆栈

 6 MAKE_FUNCTION            0
Run Code Online (Sandbox Code Playgroud)

将一个新的函数对象压入堆栈。这个动作也消耗了栈上的 prev 两个东西(这个函数的代码 obj 和它的限定名)

 8 LOAD_CONST               1 ('A')
Run Code Online (Sandbox Code Playgroud)

再次将“A”压入堆栈,以便可以用作 中的第二个参数builtins.__build_class__,即类名。

10 CALL_FUNCTION            2
Run Code Online (Sandbox Code Playgroud)

从堆栈中使用 'A' 和一个函数对象,调用__build_class__(<func>, 'A'). 操作名称后面的 2 指的是咀嚼的位置参数的数量。最右边的位置参数位于堆栈顶部,因此它们将是:类名'A',然后是 MAKE_FUNCTION 中剩余的 obj ,它体现了类定义。“下面”这些参数是 callable __build_class__,这个操作也消耗它。没有__build_class__(func, name, /, *bases, [metaclass], **kwds) -> class提供接受的可选参数。

12 STORE_NAME               0 (A)
Run Code Online (Sandbox Code Playgroud)

A = <top of stack>,本质上是在命名空间中绑定新创建的类 obj

14 LOAD_CONST               2 (None)
Run Code Online (Sandbox Code Playgroud)

RETURN_VALUE 将返回栈顶,但类语句 exec 不需要返回值,因此None在返回之前加载。

16 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

我们完成了。

要了解为什么__build_class__将函数作为第一个参数,请参阅Guido 的这篇文章。一个函数对象用于类体,我猜是为了方便实现。