defaultdict 的嵌套 defaultdict

2024-12-19 09:24:00
admin
原创
88
摘要:问题描述:有没有办法让 defaultdict 也成为 defaultdict 的默认值?(即无限级递归 defaultdict?)我希望能够做到:x = defaultdict(...stuff...) x[0][1][0] {} 因此,我可以这样做x = defaultdict(defaultdict),...

问题描述:

有没有办法让 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。

我可能最终会使用一束图案,但当我意识到我不知道如何做到这一点时,我就产生了兴趣。


解决方案 1:

这里的其他答案告诉您如何创建defaultdict包含“无限多个”的defaultdict,但它们未能解决我认为可能是您最初的需求,即简单地拥有一个两深度默认字典。

您可能一直在寻找:

defaultdict(lambda: defaultdict(dict))

您可能更喜欢这种结构的原因是:

  • 它比递归解更明确,因此读者可能更容易理解。

  • 这使得的“叶子”defaultdict可以是字典之外的东西,例如:defaultdict(lambda: defaultdict(list))defaultdict(lambda: defaultdict(set))

解决方案 2:

对于任意数量的级别:

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)

解决方案 3:

有一个巧妙的技巧可以做到这一点:

tree = lambda: defaultdict(tree)

然后您就可以用 创建您的xx = tree()

解决方案 4:

与 BrenBarn 的解决方案类似,但不包含两次变量的名称tree,因此即使在变量字典发生变化后它仍然有效:

tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))

然后您可以x使用创建每个新的x = tree()


对于该def版本,我们可以使用函数闭包作用域来保护数据结构免受名称重新绑定时现有实例停止工作的缺陷的影响tree。它看起来像这样:

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()

解决方案 5:

我还建议采用更多 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': {}}

解决方案 6:

这是一个递归函数,用于将递归默认字典转换为普通字典

def defdict_to_dict(defdict, finaldict):
    # pass in an empty dict for finaldict
    for k, v in defdict.items():
        if isinstance(v, defaultdict):
            # new level created and that is the new value
            finaldict[k] = defdict_to_dict(v, {})
        else:
            finaldict[k] = v
    return finaldict

defdict_to_dict(my_rec_default_dict, {})

解决方案 7:

我根据 Andrew 在此处的回答得出此结论。如果您希望将 json 或现有字典中的数据加载到 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

解决方案 8:

这是一个用于任意深度嵌套的任意基本默认字典的函数。

(从Can't pickle defaultdict交叉发布)

def wrap_defaultdict(instance, times=1):
    """Wrap an instance an arbitrary number of `times` to create nested defaultdict.
    
    Parameters
    ----------
    instance - list, dict, int, collections.Counter
    times - the number of nested keys above `instance`; if `times=3` dd[one][two][three] = instance
    
    Notes
    -----
    using `x.copy` allows pickling (loading to ipyparallel cluster or pkldump)
        - thanks https://stackoverflow.com/questions/16439301/cant-pickle-defaultdict
    """
    from collections import defaultdict

    def _dd(x):
        return defaultdict(x.copy)

    dd = defaultdict(instance)
    for i in range(times-1):
        dd = _dd(dd)

    return dd

解决方案 9:

@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)

解决方案 10:

但是,根据 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))

解决方案 11:

这是一个类似于@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)

解决方案 12:

这是一个类似于@Chris W. 的解决方案,它使更多级别成为可能。它仍然允许将“叶子”指定为 defaultdict 以外的其他内容。

定义的不是 lambda,而是闭包。

您可能更喜欢这种方法,因为

  • 嵌套 defaultdict 的声明被写为嵌套函数,因此可能更容易阅读。

  • 可以实现超过两个级别。

  • 最后一片叶子可以是:列表,集合,......

这是一个例子。

from collections import defaultdict
import json

def another_defaultdict(factory):
    'return another layer of defaultdict as a factory function'
    def layer():
        return defaultdict(factory)  
    return layer




>>> # two levels
>>> d = defaultdict(another_defaultdict(list))

>>> # three levels
>>> d = defaultdict(another_defaultdict(another_defaultdict(list)))


>>> d['Canada']['Alberta'] = ['Calgary', 'Magrath', 'Cardston', 'Lethbridge']
>>> d['France']['Nord'] = ['Dunkirk', 'Croix']
>>> print(json.dumps(d, indent=2))
{
  "Canada": {
    "Alberta": [
      "Calgary",
      "Magrath",
      "Cardston",
      "Lethbridge"
    ]
  },
  "France": {
    "Nord": [
      "Dunkirk",
      "Croix"
    ]
  }
}

解决方案 13:

这是一个defaultdict完全避免的解决方案。

class InfiniteDefaultDict(dict):

    def __getitem__(self, key):
        if key in self:                                                  
            value = super().__getitem__(key)                             
        else:                                                            
            value = InfiniteDefaultDict()
            super().__setitem__(key, value)                              
        return value


In [1]: d = InfiniteDefaultDict()

In [2]: d['a']['b']['c']['d']['e'] = 1

In [3]: d
Out[4]: {'a': {'b': {'c': {'d': {'e': 1}}}}}

参见https://github.com/crosscompute/crosscompute-macros/compare/b11dcd390941a9aab42930580b93c20b79e34d9d..beb8ba6a84b30c12b2c688a98f46428631d252d2

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1120  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   75  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   66  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   76  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   70  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用