对OrderedDict派生对象进行序列化

1 投票
1 回答
1369 浏览
提问于 2025-04-18 00:09

我创建了一个标准类 collections.OrderedDict 的子类。当我尝试反序列化(unpickle)这个类的对象时,出现了以下错误:

Traceback (most recent call last):
  File "pickle.py", line 29, in <module>
    print cPickle.load(f)
TypeError: ('__init__() takes exactly 1 argument (2 given)', <class '__main__.ConfiguratorsDict'>, ([['toto', 20]],))

为了弄清楚为什么会出现这样的情况,我简化了 collections.OrderedDict 的代码,得到了以下最小代码,这段代码会引发之前提到的错误。代码如下:

import cPickle

class OrderedDict(dict):
    def __reduce__(self):
        items = [[k, self[k]] for k in self]
        inst_dict = vars(self).copy()
        for k in vars(OrderedDict()):
            inst_dict.pop(k, None)
        if inst_dict:
            return (self.__class__, (items,), inst_dict)

        return self.__class__, (items,)

class ConfiguratorsDict(OrderedDict):

    def __init__(self):
        OrderedDict.__init__(self)

        self._myspec = "blabla"

if __name__ == "__main__":

    f = open("test.pickle","wb")
    c = ConfiguratorsDict()
    c["toto"] = 20
    cPickle.dump(c,f)
    f.close()    
    f = open("test.pickle","rb")
    print cPickle.load(f)
    f.close()

到目前为止,我真的不明白问题出在哪里。是我对 pickle 机制理解错了,还是与 OrderedDict 有关的问题?

非常感谢你的帮助

1 个回答

3

你没有仔细阅读__reduce__的文档:

当返回一个元组时,它的长度必须在两个到五个元素之间。可选的元素可以省略,或者可以用None来表示。这个元组的内容会像平常一样被序列化,用于在反序列化时重建对象。每个元素的含义是:

  • 一个可调用的对象,它会被用来创建对象的初始版本。元组的下一个元素将提供这个可调用对象的参数,后面的元素则提供额外的状态信息,这些信息会在之后完全重建序列化的数据时使用。

你返回了类作为可调用对象,并且第二个元素是items,所以在反序列化时,它试图把items传给这个类,从而调用__init__,但你的__init__并不接受任何参数,因此你会遇到错误。

你要么修改__init__以接受参数,要么避免把items作为第二个元素,而是用一个空元组代替。

撰写回答