Подтвердить что ты не робот

Почему определение класса всегда создает один и тот же байт-код?

Скажем, я:

#!/usr/bin/env python
# encoding: utf-8

class A(object):
    pass

Теперь я разобрал его:

python -m dis test0.py 
  4           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE        

Теперь я добавляю некоторые определения в определение класса:

#!/usr/bin/env python
# encoding: utf-8

class A(object):
    print 'hello'
    1+1
    pass

И снова разобрать:

  4           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE        

Что не появляются новые инструкции в новом байт-коде?

4b9b3361

Ответ 1

Новые операторы хранятся в вложенном байт-коде. Вы можете увидеть при разборке, что загружен другой объект кода:

      9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)

Вам нужно проверить этот объект кода. Это потому, что тело класса выполняется точно так же, как объект функции, и локальное пространство имен, которое вызывает вызовы, затем используется для формирования членов класса.

Демо:

>>> import dis
>>> def wrapper():
...     class A(object):
...         pass
... 
>>> dis.dis(wrapper)
  2           0 LOAD_CONST               1 ('A')
              3 LOAD_GLOBAL              0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               2 (<code object A at 0x104b99930, file "<stdin>", line 2>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_FAST               0 (A)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE        
>>> dis.dis(wrapper.__code__.co_consts[2])
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_LOCALS         
              7 RETURN_VALUE        

Это та же настройка, что и ваш первый образец; доступ к элементу класса осуществляется через кортеж wrapper.__code__.co_consts, к которому относится код байта LOAD_CONST; индекс указывается как 2.

Теперь мы можем добавить тело класса:

>>> def wrapper():
...     class A(object):
...         print 'hello'
...         1+1
...         pass
... 
>>> dis.dis(wrapper)
  2           0 LOAD_CONST               1 ('A')
              3 LOAD_GLOBAL              0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               2 (<code object A at 0x104b4adb0, file "<stdin>", line 2>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_FAST               0 (A)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE        
>>> dis.dis(wrapper.__code__.co_consts[2])
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_CONST               0 ('hello')
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  4          11 LOAD_CONST               2 (2)
             14 POP_TOP             

  5          15 LOAD_LOCALS         
             16 RETURN_VALUE        

Теперь появляется класс класса; мы можем видеть байт-код, который будет выполняться при загрузке тела класса.

Следует отметить, что байт-коды LOAD_NAME и STORE_NAME выполняются для каждого класса класса; те извлекают имя модуля и сохраняют их как новое локальное имя __module__, так что ваш класс будет иметь атрибут __module__ после создания.

Байт-код LOAD_LOCALS затем собирает все локальные имена, созданные в этой "функции", и возвращает их вызывающему, так что байт-код BUILD_CLASS может использовать это вместе со строкой 'A' и базой object кортеж (созданный с помощью BUILD_TUPLE) может создать ваш новый объект класса.