Рассмотрим этот код Python (версия 3.5):
import pickle
from types import SimpleNamespace
class MyClass1(list):
def __init__(self, x):
self.append(x)
class MyClass2(SimpleNamespace):
def __init__(self, x):
self.x = x
a0 = SimpleNamespace(x=99)
a1 = MyClass1(99)
a2 = MyClass2(99)
print('* SimpleNamespace:', pickle.loads(pickle.dumps(a0)))
print('* MyClass1:', pickle.loads(pickle.dumps(a1)))
print('* MyClass2:', pickle.loads(pickle.dumps(a2)))
Это отлично работает для первых двух (a0 и a1), но после обработки a2 я получаю сообщение об ошибке:
* SimpleNamespace: namespace(x=99)
* MyClass1: [99]
Traceback (most recent call last):
File "./picktest.py", line 20, in <module>
print('* MyClass2:', pickle.loads(pickle.dumps(a2)))
TypeError: __init__() missing 1 required positional argument: 'x'
Замечания:
SimpleNamespace
можно мариновать.list
), может быть (un) маринован.SimpleNamespace
не может быть разбросан, если он содержит аргументы __init__
. (Ошибка возникает в pickle.loads
) Обратите внимание, что я попытался заменить self.x = x
простым pass
и это ничего не изменило.
Есть ли способ сделать эту работу иначе, чем переопределить MyClass2 без наследования?
Проблема в том, что SimpleNamespace
определяет __reduce__
, что pickle
используется для unpickle вашего объекта. Однако __reduce__
определенный в SimpleNamespace
, не соответствует вашему __init__
. Вы можете определить свой собственный __reduce__
чтобы обойти вокруг этого:
import pickle
from types import SimpleNamespace
class MyClass1(list):
def __init__(self, x):
self.append(x)
class MyClass2(SimpleNamespace):
def __init__(self, x):
self.x = x
def __reduce__(self):
return (self.__class__, (self.x,))
a0 = SimpleNamespace(x=99)
a1 = MyClass1(99)
a2 = MyClass2(99)
print('* SimpleNamespace:', pickle.loads(pickle.dumps(a0)))
print('* MyClass1:', pickle.loads(pickle.dumps(a1)))
print('* MyClass2:', pickle.loads(pickle.dumps(a2)))