Exemplo simples de uso de __setstate__ e __getstate__

88

Não sei o que os métodos __setstate__e __getstate__fazem, então me ajude com um exemplo simples.

zjm1126
fonte
25
Os documentos não são muito bons nesse ponto.
Matt Luongo

Respostas:

72

Aqui está um exemplo muito simples para Python que deve complementar a documentação do 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
fonte
9
Para complementar essa resposta, ele imprime "Estou sendo decapado", depois "Estou sendo retirado da picagem com estes valores: {'val': 4}" e f_new.val é 12.
timidpueo
41

Exemplo mínimo

O que quer que saia getstate, entra setstate. Não precisa ser um ditado.

O que quer que sai getstatedeve ser pickeable, por exemplo composta de built-ins 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

Padrão __setstate__

O padrão __setstate__leva a dict.

self.__dict__é uma boa escolha como em https://stackoverflow.com/a/1939384/895245 , mas podemos construir um para ver melhor o que está acontecendo:

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

Padrão __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__ objetos não têm __dict__

Se o objeto tem __slots__, então ele não tem__dict__

Se você for implementar gete setstate, a maneira padrão é:

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__ get e set padrão esperam uma tupla

Se você quiser reutilizar o padrão __getstate__ou __setstate__, terá que passar as 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

Não sei para que serve.

Herança

Primeiro, veja se a decapagem funciona por padrão:

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

Herança personalizada __getstate__

Sem __slots__isso é fácil, já que o __dict__for Dcontém o __dict__for C, então não precisamos tocar Cem:

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

Herança e __slots__

Com __slots__, precisamos encaminhar para a classe base e podemos passar 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

Infelizmente não é possível reutilizar o padrão __getstate__e __setstate__da base: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ somos obrigados a defini-los.

Testado em Python 2.7.12. GitHub upstream .

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

Estes métodos são utilizados para controlar a forma como os objectos são decapados e unpickled pela salmoura módulo. Isso geralmente é feito automaticamente, então, a menos que você precise substituir a forma como uma classe é em conserva ou não, você não precisa se preocupar com isso.

Pär Wieslander
fonte