__setstate__ 和 __getstate__ 的简单使用示例

153 投票
4 回答
95227 浏览
提问于 2025-04-15 17:14

我不知道 __setstate____getstate__ 这两个方法是干什么的,能给我一个简单的例子吗?

4 个回答

18

这些方法是用来控制对象在被“腌制”(也就是序列化)和“解腌”(也就是反序列化)时的行为,主要是通过pickle模块来实现的。一般情况下,这些过程都是自动处理的,所以如果你不需要改变一个类的腌制或解腌方式,通常就不需要担心这些了。

73

最小示例

getstate 得到的东西,会传入 setstate。它不一定要是一个字典。

getstate 得到的内容必须是可以被“打包”的,比如由基本的数据类型组成,比如 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
import pickle
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1

默认的 __setstate__

默认的 __setstate__ 接受一个 dict(字典)。

self.__dict__ 是一个不错的选择,就像在 这个链接 中提到的那样,但我们也可以自己构造一个,以便更好地理解发生了什么:

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 __getstate__(self):
        return { slot: getattr(self, slot) for slot in self.__slots__ }
    def __setstate__(self, d):
        for slot in d:
            setattr(self, slot, d[slot])
assert pickle.loads(pickle.dumps(C(1), -1)).i == 1

__slots__ 默认的获取和设置期望一个元组

如果你想重用默认的 __getstate____setstate__,你需要传递元组:

class C(object):
    __slots__ = 'i'
    def __init__(self, i):
        self.i = i
    def __getstate__(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__,这很简单,因为 D__dict__ 包含了 C__dict__,所以我们根本不需要碰 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__这个链接 中提到,我们被迫定义它们。

在 Python 2.7.12 上测试过。GitHub 上的相关代码

123

这里有一个非常简单的Python示例,可以帮助你更好地理解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)

输出结果:

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

撰写回答