@Derfirm
Noname Developer.

Как иметь общий базовый класс для динамических инстансов?

Добрый день!
Развлекаюсь с динамическим созданием классов, раскрыл для себя мощь `types.new_class` и имею функци-фабрику вида
def create_instance(cls_name, module,  args=(), bases=()):
        def clsexec(ns):
        ns["__module__"] = module
        if args:
            ns["__reduce__"] = lambda self: args

    cls = types.new_class(cls_name, bases, {}, clsexec)
    return cls()


Реальный пример чуть побогаче, но важный нюанс - тут докидывается редюсе в зависимости от входных параметров. Всё работает до момента, пока я не делаю `pickle.dumps` созданных таким образом классов, например
class Sample:
     pass

main = [Sample(), Sample()]
pickle.dumps(main, protocol=2)

возвращает адекватный набор опкодов
0: \x80 PROTO      2
    2: ]    EMPTY_LIST
    3: q    BINPUT     0
    5: (    MARK
    6: c        GLOBAL     'source_module Sample'
   43: q        BINPUT     1
   45: )        EMPTY_TUPLE
   46: \x81     NEWOBJ
   47: q        BINPUT     2
   49: h        BINGET     1
   51: )        EMPTY_TUPLE
   52: \x81     NEWOBJ
   53: q        BINPUT     3
   55: e        APPENDS    (MARK at 5)
   56: .    STOP

а если скормить пиклу такие же, но динамически созданные классы
main = [create_instance("Sample", "source_code"), create_instance("Sample", "source_code")]
pickle.dumps(main, protocol=2

то будет вроде
0: \x80 PROTO      2
    2: ]    EMPTY_LIST
    3: q    BINPUT     0
    5: (    MARK
    6: c        GLOBAL     'source_module Sample'
   43: q        BINPUT     1
   45: )        EMPTY_TUPLE
   46: \x81     NEWOBJ
   47: q        BINPUT     2
   49: c        GLOBAL     'source_module Sample'
   86: q        BINPUT     3
   88: )        EMPTY_TUPLE
   89: \x81     NEWOBJ
   90: q        BINPUT     4
   92: e        APPENDS    (MARK at 5)
   93: .    STOP


Небольшой дебагинг показал путь из-за чего это происходит, а именно:
- вызывается `save` по первому инстансу, дальше мы проваливаемся в `save_reduce`
- к примеру для второй версии proto мы начинаем сохранять содержимое объекта и информацию о его классах/типах
- в первый раз попадаем под чек мемоизации, пока там пусто (так как первая встреча с классом), дальше разбираем класс через диспатчинг и честно запоминаем.

- когда приходит второй инстанс - мы опять доходим до мемоизации класса, но фейлимся так как динамически созданный инстанс также динамически создал уже другой класс и я проваливаю проверку по айди, код выполняется еще раз => в пикл еще раз пишется `GLOBAL` вместо ``BINGET`

Суть вопроса - как иметь общий класс для одинаковых сущностей из одного модуля/имени, чтобы срабатывала мемоизация для пикла, но при это не ломать динамическое определение атрибутов у инстансов?
  • Вопрос задан
  • 39 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы