Semplice esempio di utilizzo di __setstate__ e __getstate__


92

Non so cosa fanno i metodi __setstate__e __getstate__, quindi aiutami con un semplice esempio.


25
I documenti non sono comunque molto bravi su questo punto.
Matt Luongo

Risposte:


80

Ecco un esempio molto semplice per Python che dovrebbe integrare i documenti di 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)

9
Per completare questa risposta viene stampato "I'm being pickled", quindi "I'm being unpickled with these values: {'val': 4}", and f_new.val is 12.
timidpueo

45

Esempio minimo

Qualunque cosa venga fuori getstate, entra setstate. Non è necessario che sia un dettame.

Ciò che viene fuori getstatedeve essere pickeable, ad esempio costituito da built-in di base come 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

Predefinito __setstate__

L'impostazione predefinita __setstate__richiede un file dict.

self.__dict__è una buona scelta come in https://stackoverflow.com/a/1939384/895245 , ma possiamo costruirne uno noi stessi per vedere meglio cosa sta succedendo:

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

Predefinito __getstate__

Analogo a __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__ gli oggetti non hanno __dict__

Se l'oggetto ha __slots__, allora non ha__dict__

Se hai intenzione di implementare sia gete setstate, il modo predefinito è:

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__ Il valore predefinito get and set prevede una tupla

Se vuoi riutilizzare il valore predefinito __getstate__o __setstate__, dovrai passare le tuple come:

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

Non sono sicuro di cosa serva.

Eredità

Per prima cosa vedi che il decapaggio funziona di default:

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

Ereditarietà personalizzata __getstate__

Senza __slots__è facile, poiché il __dict__for Dcontiene il __dict__for C, quindi non abbiamo bisogno di toccare Caffatto:

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

Eredità e __slots__

Con __slots__, dobbiamo inoltrare alla classe base e possiamo passare le tuple in giro:

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

Purtroppo non è possibile riutilizzare il default __getstate__e __setstate__della base: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ siamo costretti a definirli.

Testato su Python 2.7.12. GitHub a monte .


10

Questi metodi vengono utilizzati per controllare come gli oggetti vengono decapati e non selezionati dal modulo pickle . Questo di solito viene gestito automaticamente, quindi, a meno che non sia necessario sovrascrivere il modo in cui una classe viene decapitata o deselezionata, non dovresti preoccupartene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.