Prosty przykład użycia __setstate__ i __getstate__

88

Nie wiem, co __setstate__i __getstate__metody nie, tak mi pomóc z prostym przykładzie.

zjm1126
źródło
25
Dokumentacja i tak nie jest zbyt dobra w tej kwestii.
Matt Luongo,

Odpowiedzi:

72

Oto bardzo prosty przykład dla Pythona, który powinien uzupełniać dokumentację 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
źródło
9
Aby uzupełnić tę odpowiedź, wypisuje "Jestem marynowany", a następnie "Jestem oderwany z tymi wartościami: {'val': 4}", a f_new.val to 12.
timidpueo
41

Minimalny przykład

Cokolwiek wychodzi getstate, wchodzi w setstate. Nie musi to być dyktando.

Cokolwiek wyjdzie getstatemusi być pickeable np składa się z podstawowych wbudowanych wtyczek takich jak 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

Domyślna __setstate__

Domyślnie __setstate__zajmuje dict.

self.__dict__to dobry wybór, jak na https://stackoverflow.com/a/1939384/895245 , ale możemy sami zbudować taki, aby lepiej widzieć, co się dzieje:

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

Domyślna __getstate__

Analogicznie do __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__ obiekty nie mają __dict__

Jeśli obiekt ma __slots__, to nie ma__dict__

Jeśli zamierzasz zaimplementować oba geti setstate, domyślnym sposobem jest:

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__ Domyślnie get and set oczekuje krotki

Jeśli chcesz ponownie użyć domyślnego __getstate__lub __setstate__, będziesz musiał przekazać krotki jako:

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

Nie jestem pewien, do czego to służy.

Dziedzictwo

Najpierw zobacz, że wytrawianie działa domyślnie:

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

Niestandardowe dziedziczenie __getstate__

Bez __slots__tego jest to łatwe, ponieważ __dict__for Dzawiera __dict__for C, więc nie musimy Cw ogóle dotykać :

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

Dziedziczenie i __slots__

W programie __slots__musimy przekazać do klasy bazowej i możemy przekazywać krotki wokół:

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

Niestety nie jest możliwe ponowne użycie wartości domyślnych __getstate__i __setstate__bazy: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ jesteśmy zmuszeni je zdefiniować.

Przetestowano w Pythonie 2.7.12. GitHub upstream .

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
9

Te metody są używane do kontrolowania sposobu wytrawiania i odblokowywania przedmiotów przez moduł marynowania . Zwykle jest to obsługiwane automatycznie, więc nie musisz się tym martwić, chyba że musisz zmienić sposób, w jaki klasa jest marynowana lub odmładzana.

Pär Wieslander
źródło