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

Bjö*_*lex 10 python pickle

我有一个继承自defaultdict这样的类:

class listdict(defaultdict):
    def __init__(self):
        defaultdict.__init__(self, list)
Run Code Online (Sandbox Code Playgroud)

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

('__init__() takes exactly 1 argument (2 given)', <class 'listdict'>, (<type 'list'>,))
Run Code Online (Sandbox Code Playgroud)

该类没有定义pickle协议的任何特殊方法.正常的酸洗和去除defaultdict(list)工作按预期工作.任何人都可以开导我吗?

Tho*_*ers 8

类型通过定义一组(相当大)的方法来定义它的实例如何被腌制.每个都有自己的微妙行为.查看pickle协议的文档.在这种情况下collections.defaultdict,它使用的__reduce__方法:

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

元组中的第一项是类型,第二项是在实例化时传递给类型的参数元组.如果不覆盖__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
Run Code Online (Sandbox Code Playgroud)

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

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

def listdict():
    return collections.defaultdict(list)
Run Code Online (Sandbox Code Playgroud)