Ejemplo simple de uso de __setstate__ y __getstate__

88

No sé qué hacen los métodos __setstate__y __getstate__, así que ayúdame con un ejemplo simple.

zjm1126
fuente
25
De todos modos, los documentos no son muy buenos en este punto.
Matt Luongo

Respuestas:

72

Aquí hay un ejemplo muy simple de Python que debería complementar los documentos de 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
fuente
9
Para complementar esta respuesta, imprime "Me están encurtidos", luego "Me están deshaciendo con estos valores: {'val': 4}", y f_new.val es 12.
timidpueo
41

Ejemplo mínimo

De todo lo que sale getstate, entra setstate. No tiene por qué ser un dict.

Lo que sale de getstatedebe ser pickeable, por ejemplo, formada por muebles empotrados básicos como 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

Defecto __setstate__

El valor predeterminado __setstate__toma un dict.

self.__dict__es una buena opción como en https://stackoverflow.com/a/1939384/895245 , pero podemos construir uno nosotros mismos para ver mejor lo que está sucediendo:

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

Defecto __getstate__

Análogo 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__ los objetos no tienen __dict__

Si el objeto tiene __slots__, entonces no tiene__dict__

Si va a implementar ambos gety setstate, la forma predeterminada es:

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__ predeterminado get y set espera una tupla

Si desea reutilizar el valor predeterminado __getstate__o __setstate__, tendrá que pasar tuplas como:

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

No estoy seguro para qué es esto.

Herencia

Primero vea que el decapado funciona por defecto:

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

Herencia personalizada __getstate__

Sin __slots__ella es fácil, ya que la __dict__for Dcontiene la __dict__for C, por lo que no necesitamos tocar Cen absoluto:

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

Herencia y __slots__

Con __slots__, necesitamos reenviar a la clase base y podemos pasar tuplas:

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

Lamentablemente no es posible reutilizar los predeterminados __getstate__y __setstate__de la base: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ nos vemos obligados a definirlos.

Probado en Python 2.7.12. GitHub aguas arriba .

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

Estos métodos se utilizan para controlar cómo el módulo de encurtidos encurtidos y despemados los objetos . Por lo general, esto se maneja automáticamente, por lo que, a menos que necesite anular la forma en que se encurtió o despejó una clase, no debería tener que preocuparse por eso.

Pär Wieslander
fuente