Простий приклад використання __setstate__ та __getstate__


88

Я не знаю, що роблять методи __setstate__та __getstate__, тому допоможіть мені на простому прикладі.


25
Документи в цьому випадку не дуже хороші.
Matt Luongo,

Відповіді:


72

Ось дуже простий приклад для Python, який повинен доповнити документи для розсолу .

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)

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__приймається a 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 and 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

Ці методи використовуються для управління , як об'єкти мариновані і unpickled в маринаді модуля. Зазвичай це обробляється автоматично, тому, якщо вам не потрібно перевизначити спосіб маринування або розібрання класу, вам не потрібно турбуватися про це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.