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 位上运行这个
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 的这篇文章。一个函数对象用于类体,我猜是为了方便实现。