我有一个递归嵌套的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

这到底是怎么回事?

最佳答案

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({})})})

关于python - 无法 pickle 递归嵌套的defaultdict,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51991503/

10-12 16:56
查看更多