对象版本化的序列化

12 投票
2 回答
3730 浏览
提问于 2025-04-15 19:54

我正在做一个项目,这个项目需要把很多对象进行序列化,然后存储到硬盘上,使用的是 pickle 或者 cPickle

随着项目的进展(在客户使用后),将来可能会需要对一些已经保存的对象进行修改,比如添加字段、删除字段,或者只是改变某些数据的规则。

有没有什么标准的方法可以标记一个将要被序列化的对象,让它有一个特定的版本号(就像Java中的 serialVersionUID)?简单来说,如果我正在恢复一个版本为234的Foo对象,但当前代码是236,我希望在反序列化的时候能收到一些通知。我是不是应该自己想个办法来解决这个问题(这可能会很麻烦)。

谢谢

2 个回答

6

考虑一下Tomasz Früboes建议的这个类混入(mixin)。你可以在这里找到更多信息。

# versionable.py
class Versionable(object):
    def __getstate__(self):
        if not hasattr(self, "_class_version"):
            raise Exception("Your class must define _class_version class variable")
        return dict(_class_version=self._class_version, **self.__dict__)
    def __setstate__(self, dict_):
        version_present_in_pickle = dict_.pop("_class_version")
        if version_present_in_pickle != self._class_version:
            raise Exception("Class versions differ: in pickle file: {}, "
                            "in current class definition: {}"
                            .format(version_present_in_pickle,
                                    self._class_version))
        self.__dict__ = dict_

__getstate__方法在进行数据保存(也叫“序列化”)时会被pickle调用,而__setstate__方法在数据恢复(也叫“反序列化”)时会被pickle调用。这个混入类可以作为你想要跟踪版本的类的子类使用。使用方法如下:

# bla.py
from versionable import Versionable
import pickle

class TestVersioning(Versionable):
    _class_version = 1

t1 = TestVersioning()

t_pickle_str = pickle.dumps(t1)

class TestVersioning(Versionable):
    _class_version = 2

t2 = pickle.loads(t_pickle_str) # Throws exception about wrong class version
7

pickle格式没有这样的限制。为什么不把“序列版本号”作为对象的属性之一,这样在进行序列化时就可以和其他内容一起保存呢?这样一来,通过比较实际版本和期望版本,就能轻松得到“通知”——我不明白为什么这会变得这么麻烦。

撰写回答