使用__setstate__和__getstate__的简单示例

zjm*_*126 65 python serialization

我不知道__setstate____getstate__方法有什么关系,所以请帮我一个简单的例子.

Bra*_*ore 60

这是Python 2的一个非常简单的例子,它应该补充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)
Run Code Online (Sandbox Code Playgroud)

  • 虽然这只是一个演示,但演示一些不应该在实际代码中完成的事情仍然是一个坏主意。即在对象进行酸洗时使对象发生突变。在“__getstate__”期间从对象获取数据并用它(并且仅此)进行恶作剧而不改变原始对象会很好,但这不是这里发生的情况。 (16认同)
  • 为了补充这个答案,它会打印“我正在被腌制”,然后“我正在用这些值取消腌制:{'val':4}”,并且 f_new.val 是 12。 (13认同)

Cir*_*四事件 29

最小的例子

无论发生什么getstate,进入setstate.它不需要是一个字典.

无论出来的getstate一定是pickeable,例如由基本的内置插件一样的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
Run Code Online (Sandbox Code Playgroud)

默认 __setstate__

默认__setstate__需要一个dict.

self.__dict__是一个很好的选择,如/sf/answers/135756911/,但我们可以自己构建一个,以便更好地了解发生了什么:

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
Run Code Online (Sandbox Code Playgroud)

默认 __getstate__

类似于__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
Run Code Online (Sandbox Code Playgroud)

__slots__ 对象没有 __dict__

如果对象有__slots__,那么它没有__dict__

如果你要同时实现getsetstate中,默认上下的方法是:

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
Run Code Online (Sandbox Code Playgroud)

__slots__ 默认的get和set期望一个元组

如果你想重新使用默认__getstate__或者__setstate__,你将不得不通过周围的元组为:

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
Run Code Online (Sandbox Code Playgroud)

我不确定这是为了什么.

遗产

首先看到酸洗工作默认情况下:

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
Run Code Online (Sandbox Code Playgroud)

继承自定义 __getstate__

没有__slots__它很容易,因为__dict__for D包含__dict__for C,所以我们根本不需要触摸C:

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
Run Code Online (Sandbox Code Playgroud)

继承与传承 __slots__

有了__slots__,我们需要转发到基类,并可以传递元组:

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
Run Code Online (Sandbox Code Playgroud)

遗憾的是,无法重复使用默认值__getstate____setstate__基数:https://groups.google.com/forum/#!topic/python-subas/QkvOwa1-pHQ我们不得不定义它们.

在Python 2.7.12上测试过.GitHub上游.


Pär*_*der 13

这些方法用于控制pickle模块如何腌制和取消腌制对象。这通常是自动处理的,因此除非您需要覆盖类是如何腌制或取消腌制的,否则您无需担心。


Azm*_*sov 7

对@BrainCore 答案的澄清。实际上,您可能不想修改selfinside __getstate__。相反,构造一个将被腌制的新对象,保持原始对象不变以供进一步使用。看起来是这样的:

import pickle

class Foo:
    def __init__(self, x:int=2, y:int=3):
        self.x = x
        self.y = y
        self.z = x*y

    def __getstate__(self):
        # Create a copy of __dict__ to modify values and return;
        # you could also construct a new dict (or other object) manually
        out = self.__dict__.copy()
        out["x"] *= 3
        out["y"] *= 10
        # You can remove attributes, but note they will not get set with
        # some default value in __setstate__ automatically; you would need
        # to write a custom __setstate__ method yourself; this might be
        # useful if you have unpicklable objects that need removing, or perhaps
        # an external resource that can be reloaded in __setstate__ instead of
        # pickling inside the stream
        del out["z"]
        return out

    # The default __setstate__ will update Foo's __dict__;
    # so no need for a custom implementation here if __getstate__ returns a dict;
    # Be aware that __init__ is not called by default; Foo.__new__ gets called,
    # and the empty object is modified by __setstate__

f = Foo()
f_str = pickle.dumps(f)
f2 = pickle.loads(f_str)

print("Pre-pickle:", f.x, f.y, hasattr(f,"z"))
print("Post-pickle:", f2.x, f2.y, hasattr(f2,"z"))
Run Code Online (Sandbox Code Playgroud)