无法腌制递归嵌套的defaultdict

时间:2018-08-23 17:43:35

标签: python inheritance pickle defaultdict

我有一个递归嵌套的defaultdict类,定义为

from collections import defaultdict

class NestedDict(defaultdict):
    def __init__(self):
        super().__init__(self.__class__)

坐在nested_dict.py文件中。

当我尝试腌制时,例如

import pickle
from nested_dict import NestedDict

d = NestedDict()
pickle.loads(pickle.dumps(d))

我得到TypeError: __init__() takes 1 positional argument but 2 were given

这里到底发生了什么?

1 个答案:

答案 0 :(得分:2)

defaultdict类实现了object.__reduce__() method,其中返回的元组的第二个元素(构造函数的参数)将始终是工厂对象:

>>> d = NestedDict()
>>> d.__reduce__()
(<class '__main__.NestedDict'>, (<class '__main__.NestedDict'>,), None, None, <dict_itemiterator object at 0x110df59a8>)

该参数然后传递给NestedDict()调用以重建对象。抛出异常是因为NestedDict类不接受参数。

您可以在子类中覆盖__reduce__方法:

class NestedDict(defaultdict):
    def __init__(self):
        super().__init__(self.__class__)
    def __reduce__(self):
        return (type(self), (), None, None, iter(self.items()))

上面的代码产生了与defaultdict.__reduce__()返回的元素完全相同的元素,只是第二个元素现在是一个空元组。

您也可以只接受并忽略一个参数:

class NestedDict(defaultdict):
    def __init__(self, _=None):  # accept a factory and ignore it
        super().__init__(self.__class__)

_名称通常用来表示我忽略了此值

另一种实现方式可以只是子类dict并提供自定义__missing__ method;对于不在字典中的键调用此方法:

class NestedDict(dict):
    def __missing__(self, key):
        nested = self[key] = type(self)()
        return nested
    def __repr__(self):
        return f'{type(self).__name__}({super().__repr__()})'

这与您的版本完全一样,但是不需要其他的pickle支持方法:

>>> d = NestedDict()
>>> d['foo']
NestedDict({})
>>> d['foo']['bar']
NestedDict({})
>>> d
NestedDict({'foo': NestedDict({'bar': NestedDict({})})})
>>> pickle.loads(pickle.dumps(d))
NestedDict({'foo': NestedDict({'bar': NestedDict({})})})