Добрый день!
Развлекаюсь с динамическим созданием классов, раскрыл для себя мощь `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`
Суть вопроса - как иметь общий класс для одинаковых сущностей из одного модуля/имени, чтобы срабатывала мемоизация для пикла, но при это не ломать динамическое определение атрибутов у инстансов?