Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Вот очень простой пример для Python 2, который должен дополнить pickle docs.

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:", d
     self.__dict__ = d
     self.val *= 3

import pickle
f = Foo()
f_string = pickle.dumps(f)
f_new = pickle.loads(f_string)

Ответ 2

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

Что бы ни выходило из 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__ - хороший выбор, как и в fooobar.com/questions/149196/..., но мы можем self.__dict__ его сами, чтобы лучше увидеть, что происходит:

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, setstate по умолчанию ish:

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__ default 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 вверх по течению.

Ответ 3

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