Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why disassembly a python class definition shows two identical LOAD_CONST with the class name?

I would like to know why does python makes a LOAD_CONST with the same value (the class name) twice when a class is defined. When I run this code:

from dis import dis

dis("class A(): pass")

This is the output I get:

  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

As you can see in the line 3 and 5 there are two LOAD_CONST with the class name.

Why make a second LOAD_CONST with the same data if the class name was already loaded? Does this has something to do with the MAKE_FUNCTION between those LOAD_CONST?

I'm running this on python 3.7.4 64-bit

like image 221
Jorge Morgado Avatar asked Mar 02 '23 00:03

Jorge Morgado


1 Answers

TL;DR: type creation in CPython temporarily uses a function object for the class body. The first "A" is used for this function's name. The second "A" is used for the class name.

The rest of the post explains this disassembly in detail:

 0 LOAD_BUILD_CLASS

Pushes builtins.__build_class__ onto the stack. It is later called by CALL_FUNCTION to construct a class.

 2 LOAD_CONST               0 (<code object A at 0xCAFEF00D, file "<dis>", line 1>)

Pushes a code obj onto the stack (this actually contains the parsed class block - read on)

 4 LOAD_CONST               1 ('A')

Pushes 'A' onto the stack

 6 MAKE_FUNCTION            0

Pushes a new function object on the stack. This action also consumes the prev two things on the stack (the code obj for this function and its qualified name)

 8 LOAD_CONST               1 ('A')

Pushes 'A' onto the stack again so that can be used as the second argument in builtins.__build_class__, i.e. the class name.

10 CALL_FUNCTION            2

Consumes 'A' and a function object from the stack, calling __build_class__(<func>, 'A'). The 2 after the op name is referring to the number of positional arguments munched. Right-most positional argument is on top of stack, so they will be: the class name 'A', and then the obj remaining from MAKE_FUNCTION which embodies the class definition. "Underneath" those arguments is the callable __build_class__, which this op also consumes. None of the optional arguments accepted by __build_class__(func, name, /, *bases, [metaclass], **kwds) -> class were provided.

12 STORE_NAME               0 (A)

A = <top of stack>, essentially binding the newly created class obj in namespace

14 LOAD_CONST               2 (None)

RETURN_VALUE will return the top of stack, but a class statements exec doesn't need a return value, so load None before returning.

16 RETURN_VALUE

We're done.

To understand why __build_class__ takes a function as the first argument, refer to this post from Guido. A function object is used for the class body, for convenience of implementation I guess.

like image 131
wim Avatar answered Mar 04 '23 14:03

wim