Минимальный пример
Все, что выходит из getstate
, входит в setstate
. Это не должно быть диктом.
Все, что выходит из getstate
, должно быть доступно для выбора, например, состоит из базовых встроенных модулей, таких как int
, str
, list
.
class C(object):
def __init__(self, i):
self.i = i
def __getstate__(self):
return self.i
def __setstate__(self, i):
self.i = i
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1
По умолчанию __setstate__
По умолчанию __setstate__
принимает dict
.
self.__dict__
- хороший выбор, как в https://stackoverflow.com/a/1939384/895245, но мы можем построить его сами, чтобы лучше увидеть, что происходит:
class C(object):
def __init__(self, i):
self.i = i
def __getstate__(self):
return {'i': self.i}
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1
По умолчанию __getstate__
Аналогично __setstate__
.
class C(object):
def __init__(self, i):
self.i = i
def __setstate__(self, d):
self.i = d['i']
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1
__slots__
объекты не имеют __dict__
Если объект имеет __slots__
, то он не имеет __dict__
Если вы собираетесь реализовать как get
, так и setstate
, по умолчанию используется следующий способ:
class C(object):
__slots__ = 'i'
def __init__(self, i):
self.i = i
def __getsate__(self):
return { slot: getattr(self, slot) for slot in self.__slots__ }
def __setsate__(self, d):
for slot in d:
setattr(self, slot, d[slot])
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1
__slots__
по умолчанию get и set ожидает кортеж
Если вы хотите повторно использовать значения по умолчанию __getstate__
или __setstate__
, вам придется передавать кортежи как:
class C(object):
__slots__ = 'i'
def __init__(self, i):
self.i = i
def __getsate__(self):
return (None, { slot: getattr(self, slot) for slot in self.__slots__ })
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1
Я не уверен, для чего это.
Наследование
Сначала убедитесь, что травление работает по умолчанию:
class C(object):
def __init__(self, i):
self.i = i
class D(C):
def __init__(self, i, j):
super(D, self).__init__(i)
self.j = j
d = pickle.loads(pickle.dumps(D(1, 2), -1))
assert d.i == 1
assert d.j == 2
Пользовательское наследование __getstate__
Без __slots__
это просто, поскольку __dict__
для D
содержит __dict__
для C
, поэтому нам не нужно вообще нажимать C
:
class C(object):
def __init__(self, i):
self.i = i
class D(C):
def __init__(self, i, j):
super(D, self).__init__(i)
self.j = j
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
self.__dict__ = d
d = pickle.loads(pickle.dumps(D(1, 2), -1))
assert d.i == 1
assert d.j == 2
Наследование и __slots__
С __slots__
нам нужно перейти к базовому классу, и мы можем передавать кортежи:
class C(object):
__slots__ = 'i'
def __init__(self, i):
self.i = i
def __getstate__(self):
return { slot: getattr(self, slot) for slot in C.__slots__ }
def __setstate__(self, d):
for slot in d:
setattr(self, slot, d[slot])
class D(C):
__slots__ = 'j'
def __init__(self, i, j):
super(D, self).__init__(i)
self.j = j
def __getstate__(self):
return (
C.__getstate__(self),
{ slot: getattr(self, slot) for slot in self.__slots__ }
)
def __setstate__(self, ds):
C.__setstate__(self, ds[0])
d = ds[1]
for slot in d:
setattr(self, slot, d[slot])
d = pickle.loads(pickle.dumps(D(1, 2), -1))
assert d.i == 1
assert d.j == 2
К сожалению, невозможно использовать по умолчанию __getstate__
и __setstate__
базы: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ мы вынуждены их определить.
Проверено на Python 2.7.12. GitHub upstream .