__построить_класс__ на Python >= 3.5

#python

#python

Вопрос:

У меня проблемы с созданием класса вручную в более новых версиях Python.

 class A:
  x = 13
  print(locals())

print(A.x)

def f():
  __module__ = '__main__'
  __qualname__ = 'B'
  x = 13
  print(locals())

B = __build_class__(f, 'B')
print(hasattr(B, 'x'))

# just for debugging
import inspect
import dis
dis.dis(inspect.currentframe().f_code)
  

Байт-код выглядит в основном таким же, но у класса B нет атрибута x в конце. Я что-то упускаю? В более старых версиях мне приходилось возвращать только словарь, но, по-видимому, он изменился.

Комментарии:

1. Есть ли причина, по которой вам нужно использовать частный API здесь, а не типичное определение / создание экземпляра класса?

Ответ №1:

Проблема решена!

Похоже, проблема заключалась в флаге CO_NEWLOCAL. Согласно документации:

If set, a new dict will be created for the frame’s f_locals when the code object is executed.

Звучит как раз то, чего я не хочу, чтобы произошло. Если заглянуть в код CPython, то есть ровно одно место, где установлен этот флаг, при компиляции функциональных блоков. Итак, похоже, что ни одна функция здесь не будет работать. То же самое для лямбд. Но как насчет compile встроенного?

 >> compile('x = 99', '', 'exec').co_flags
64
  

Бинго! В конце концов, это фрагмент кода, который решает мою проблему:

 import types

class A:
  x = 13

print(A.x)

f = types.FunctionType(compile('x = y', '', 'exec'), globals={'y': 13})

B = __build_class__(f, 'B')
print(B.x)