Exemple simple d'utilisation de __setstate__ et __getstate__

88

Je ne sais pas ce que font les méthodes __setstate__et __getstate__, alors aidez-moi avec un exemple simple.

zjm1126
la source
25
De toute façon, les documents ne sont pas très bons sur ce point.
Matt Luongo

Réponses:

72

Voici un exemple très simple pour Python qui devrait compléter la documentation 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
la source
9
Pour compléter cette réponse, il affiche "Je suis en train d'être mariné", puis "Je suis décoché avec ces valeurs: {'val': 4}", et f_new.val vaut 12.
timidpueo
41

Exemple minimal

Tout ce qui sort de getstate, entre setstate. Il n'est pas nécessaire que ce soit un dict.

Tout ce qui vient de getstatedoit être pickeable, par exemple composé de Encastrements de base comme 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

Défaut __setstate__

La valeur par défaut __setstate__prend un dict.

self.__dict__est un bon choix comme dans https://stackoverflow.com/a/1939384/895245 , mais nous pouvons en construire un nous-mêmes pour mieux voir ce qui se passe:

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

Défaut __getstate__

Analogue à __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__ les objets n'ont pas __dict__

Si l'objet a __slots__, alors il n'a pas__dict__

Si vous allez implémenter les deux getet setstate, la méthode par défaut est:

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__ par défaut, get et set attend un tuple

Si vous souhaitez réutiliser la valeur par défaut __getstate__ou __setstate__, vous devrez passer des tuples comme suit:

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

Je ne sais pas à quoi ça sert.

Héritage

Commencez par voir que le décapage fonctionne par défaut:

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

Héritage personnalisé __getstate__

Sans __slots__c'est facile, puisque le __dict__for Dcontient le __dict__for C, nous n'avons donc pas besoin de toucher Cdu tout:

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

Héritage et __slots__

Avec __slots__, nous devons transmettre à la classe de base et pouvons passer des tuples:

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

Malheureusement il n'est pas possible de réutiliser les valeurs par défaut __getstate__et __setstate__de la base: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ nous sommes obligés de les définir.

Testé sur Python 2.7.12. GitHub en amont .

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
9

Ces méthodes sont utilisées pour contrôler la façon dont les objets sont décapés et décapés par le module pickle . Ceci est généralement géré automatiquement, donc à moins que vous n'ayez besoin de remplacer la façon dont une classe est décapée ou décochée, vous ne devriez pas avoir à vous en soucier.

Pär Wieslander
la source