__setstate__ 和__ getstate__ 的简单使用示例

我不知道 __setstate____getstate__方法是做什么的,所以请给我一个简单的例子。

73073 次浏览

这些方法用于控制 泡菜模块对对象进行 pickle 和 unpickle 的方式。这通常是自动处理的,所以除非您需要重写类是如何 pickle 或 unpickle 的,否则不必担心。

下面是 Python 的一个非常简单的示例,它应该补充 腌黄瓜医生

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)

产出:

I'm being pickled
I'm being unpickled with these values: {'val': 4}

最小的例子

无论从 getstate出来的是什么,都会进入 setstate。它不需要是一个结果。

无论从 getstate出来的是什么,都必须是可选的,例如由基本的内置功能如 intstrlist组成。

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

默认 __setstate__

默认的 __setstate__采用 dict

self.__dict__https://stackoverflow.com/a/1939384/895245一样是一个不错的选择,但我们可以自己构建一个来更好地了解正在发生的情况:

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

默认 __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

__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

__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

我不知道这是干什么用的。

遗产

首先看看酸洗工程默认情况:

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

继承自定义 __getstate__

没有 __slots__很容易,因为 __dict__D包含 __dict__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

继承和 __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

遗憾的是,不可能重用基本的缺省 __getstate____setstate__: https://groups.google.com/forum/#!topic/python-ideas/QkvOwa1-pHQ,我们被迫定义它们。

在 Python 2.7.12. GitHub 上游上进行了测试。

对@BrainCore 回答的澄清。实际上,您可能不希望修改 __getstate__内部的 self。相反,应该构造一个新对象,该对象将被 pickle,原始对象保持不变,以便进一步使用。下面是这样的:

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"))