Простой пример использования __setstate__ и __getstate__ - PullRequest
56 голосов
/ 21 декабря 2009

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

Ответы [ 3 ]

52 голосов
/ 21 декабря 2009

Вот очень простой пример для 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)
23 голосов

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

Все, что выходит из 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 .

4 голосов
/ 21 декабря 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...