Простой пример использования __setstate__ и __getstate__

88

Я не знаю , что __setstate__и __getstate__методы делают, так что помочь мне с простым примером.

zjm1126
источник
25
В любом случае документы не очень хороши по этому поводу.
Мэтт Луонго

Ответы:

72

Вот очень простой пример для Python, который должен дополнять документацию по pickle .

class Foo(object):
  def __init__(self, val=2):
     self.val = val
  def __getstate__(self):
     print("I'm being pickled")
     self.val *= 2
     return self.__dict__
  def __setstate__(self, d):
     print("I'm being unpickled with these values: " + repr(d))
     self.__dict__ = d
     self.val *= 3

import pickle
f = Foo()
f_data = pickle.dumps(f)
f_new = pickle.loads(f_data)
BrainCore
источник
9
Чтобы дополнить этот ответ, он печатает «Меня маринуют», затем «Меня не выбирают с этими значениями: {'val': 4}», и f_new.val равно 12.
timidpueo
41

Минимальный пример

Все, что выходит getstate, входит вsetstate . Это не обязательно должен быть диктат.

Все , что выходит из getstateдолжны быть pickeable, например , из основных встроенных функций , таких как 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__for Dсодержит __dict__for 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 вверх по течению .

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
9

Эти методы используются для управления процессом маринования и освобождения объектов модулем pickle . Обычно это обрабатывается автоматически, поэтому, если вам не нужно переопределять способ маринования или выделения класса, вам не нужно об этом беспокоиться.

Пэр Вислендер
источник