有没有办法让defaultdict也成为defaultdict的默认值?(即无限级递归defaultdict?)

我希望能够做到:

x = defaultdict(...stuff...)
x[0][1][0]
{}

所以,我可以做x = defaultdict(defaultdict)但这只是第二层:

x[0]
{}
x[0][0]
KeyError: 0

有些食谱可以做到这一点。但是仅仅使用普通的defaultdict参数就可以做到吗?

注意,这是在询问如何做一个无限级递归defaultdict,所以它与Python不同:defaultdict of defaultdict?,这是如何做一个两级defaultdict。

我可能最终会使用串模式,但当我意识到我不知道如何做时,它让我感兴趣。


当前回答

我还建议采用更多oop风格的实现,它支持无限嵌套以及正确格式化的repr。

class NestedDefaultDict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)

    def __repr__(self):
        return repr(dict(self))

用法:

my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']

print(my_dict)  # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}

其他回答

然而,根据Chris W的回答,为了解决类型注释问题,您可以将其作为定义详细类型的工厂函数。例如,当我研究这个问题时,这是我问题的最终解决方案:

def frequency_map_factory() -> dict[str, dict[str, int]]:
    """
    Provides a recorder of: per X:str, frequency of Y:str occurrences.
    """
    return defaultdict(lambda: defaultdict(int))

这里有一个类似于@Stanislav的答案的解决方案,适用于多处理,也允许终止嵌套:

from collections import defaultdict
from functools import partial

class NestedDD(defaultdict):
    def __init__(self, n, *args, **kwargs):
        self.n = n
        factory = partial(build_nested_dd, n=n - 1) if n > 1 else int
        super().__init__(factory, *args, **kwargs)

    def __repr__(self):
        return repr(dict(self))

def build_nested_dd(n):
    return NestedDD(n)

@nucklehead的响应可以扩展到处理JSON中的数组:

def nested_dict(existing=None, **kwargs):
    if existing is None:
        existing = defaultdict()
    if isinstance(existing, list):
        existing = [nested_dict(val) for val in existing]
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_dict(val) for key, val in existing.items()}
    return defaultdict(nested_dict, existing, **kwargs)

我基于安德鲁的回答。 如果你想从json或现有的dict中加载数据到nester defaultdict中,请看这个例子:

def nested_defaultdict(existing=None, **kwargs):
    if existing is None:
        existing = {}
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_defaultdict(val) for key, val in existing.items()}
    return defaultdict(nested_defaultdict, existing, **kwargs)

https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775

对于任意数量的层:

def rec_dd():
    return defaultdict(rec_dd)

>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}

当然你也可以用lambda来做这个,但是我发现lambda可读性较差。在任何情况下,它看起来是这样的:

rec_dd = lambda: defaultdict(rec_dd)