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

96
zjm1126 21 dic. 2009 a las 12:17

3 respuestas

La mejor respuesta

Aquí hay un ejemplo muy simple para Python 2 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:", d
     self.__dict__ = d
     self.val *= 3

import pickle
f = Foo()
f_string = pickle.dumps(f)
f_new = pickle.loads(f_string)
80
Mike T 21 oct. 2020 a las 00:01

Estos métodos se utilizan para controlar cómo los objetos son encurtidos y desenredados por el pickle módulo. Esto generalmente se maneja automáticamente, por lo que, a menos que necesite anular la forma en que una clase se conserva en vinagre o no, no debe preocuparse por eso.

11
Pär Wieslander 21 dic. 2009 a las 09:25

Ejemplo mínimo

Lo que salga de getstate, entra en setstate. No necesita ser un dict.

Lo que sea que salga de getstate debe ser pickeable, p. compuesto por elementos integrados 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

Predeterminado __setstate__

La __setstate__ predeterminada 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

Predeterminado __getstate__

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

Si el objeto tiene __slots__, entonces no tiene __dict__

Si va a implementar tanto get como 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 and set espera una tupla

Si desea reutilizar los __getstate__ o __setstate__ predeterminados, deberá pasar las 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 de para qué sirve 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__ es fácil, ya que __dict__ para D contiene el __dict__ para C, por lo que no necesitamos tocar C en 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 __getstate__ y __setstate__ predeterminados 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.

46
Ciro Santilli TRUMP BAN IS BAD 25 ene. 2019 a las 18:09