如何pickle和unpickle从defaultdict继承的类的实例?

时间:2010-10-04 12:57:23

标签: python pickle

我有一个继承自defaultdict的类:

class listdict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, list)

我可以腌制它,但当我取消它时,会发生这种情况:

('__init__() takes exactly 1 argument (2 given)', <class 'listdict'>, (<type 'list'>,))

该类没有定义pickle协议的任何特殊方法。普通defaultdict(list)的酸洗和去除工作符合预期。谁能开导我?

2 个答案:

答案 0 :(得分:8)

类型通过定义一组(相当大)的方法来定义它的实例如何被腌制。每个都有自己的微妙行为。见the docs on the pickle protocol。对于collections.defaultdict,它使用__reduce__方法:

>>> l = collections.defaultdict(list)
>>> l.__reduce__()
(<type 'collections.defaultdict'>, (<type 'list'>,), None, None, <dictionary-itemiterator object at 0x7f031fb3c470>)

元组中的第一项是类型,第二项是在实例化时传递给类型的参数元组。如果您不覆盖__reduce__,则第一项将正确更改为您的类型,但第二项不会。这会导致您看到的错误。一个粗略的例子,说明如何解决它:

>>> import collections
>>> import pickle
>>> class C(collections.defaultdict):
...     def __init__(self):
...         collections.defaultdict.__init__(self, list)
...     def __reduce__(self):
...         t = collections.defaultdict.__reduce__(self)
...         return (t[0], ()) + t[2:]
...
>>> c = C()
>>> c[1].append(2)
>>> c[2].append(3)
>>> c2 = pickle.loads(pickle.dumps(c))
>>> c2 == c
True

这只是一个粗略的例子,因为还有更多的酸洗(如__reduce_ex__),而且这一切都相当复杂。在这种情况下,使用__getinitargs__可能会更方便。

或者,您可以让您的班级__init__方法使用可选可调用,默认为list,或者您可以只使用函数而不是类:

def listdict():
    return collections.defaultdict(list)

答案 1 :(得分:-1)

该错误表明你的'listdict'类需要一个参数(隐式self),但有两个参数。

您的类继承自defaultdict,并定义了初始化程序。这个初始化程序调用defaultdict的初始化程序并将'list'传递给它,在这种情况下可以是函数或类。 (我不能打扰检查)。

你可能意味着这样做:

class listdict(defaultdict):
    def __init__(self, list):
        defaultdict.__init__(self, list)

现在,当listdict使用给定列表初始化时,它会将THAT列表传递给defaultdict的构造函数,而不是将引用传递给全局列表。

(也就是说,使用与普通全局方法和类相同的名称(例如'str','list'等)被认为是错误的样式,原因与您感到困惑的原因相同。)