【问题标题】:Nested defaultdict of defaultdictdefaultdict的嵌套defaultdict
【发布时间】:2013-10-11 22:15:05
【问题描述】:

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

我可能最终会使用 bunch 模式,但当我意识到我不知道如何做到这一点时,它让我产生了兴趣。

【问题讨论】:

标签: python recursion defaultdict


【解决方案1】:

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

您可能一直在寻找:

defaultdict(lambda: defaultdict(dict))

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

  • 它比递归解决方案更明确,因此读者可能更容易理解。
  • 这使得defaultdict 的“叶子”可以不是字典,例如:defaultdict(lambda: defaultdict(list))defaultdict(lambda: defaultdict(set))

【讨论】:

  • defaultdict(lambda: defaultdict(list)) 正确的形式?
  • 哎呀,是的,lambda 形式是正确的——因为 defaultdict(something) 返回一个类似字典的对象,但 defaultdict 需要一个可调用对象!谢谢!
  • 这被标记为可能与另一个问题重复......但这不是我原来的问题。我知道如何创建一个两级默认字典;我不知道如何使它递归。这个答案,其实和stackoverflow.com/questions/5029934/…类似
  • lambda 方法的一个缺点是它生成的对象不能被腌制......但你可以通过在腌制之前强制转换为常规 dict(result) 来解决这个问题
  • 这似乎只适用于大于 1 的递归级别。例如 nested_dict['foo']['bar'].append('baz') 有效,但 nested_dict['foo'].append('bar') 失败,因为 defaultdict 类没有 append 属性
【解决方案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)

【讨论】:

  • 确实是一个完美的例子,谢谢。您能否将其扩展为将数据从 json 加载到 defaultdict 的 defaultdict 的情况?
  • 一张便条。如果您在酸洗 lambda 时尝试使用此代码将不起作用。
  • 如何定义字典底部最后一个值的类型?
  • 很好的解决方案!
【解决方案3】:

这样做有一个绝妙的技巧:

tree = lambda: defaultdict(tree)

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

【讨论】:

  • 回到这个,我认为这是我使用最多的一段代码:)
【解决方案4】:

类似于 BrenBarn 的解决方案,但不包含两次变量 tree 的名称,因此即使在更改变量字典后也能正常工作:

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

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


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

from collections import defaultdict

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

【讨论】:

  • 我将不得不考虑这个(它有点复杂)。但我认为你的意思是,如果 x = tree(),但后来有人过来并且 tree=None,这个仍然可以工作,那不会?
【解决方案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': {}}

【讨论】:

  • 整洁!我添加了*args**kwargs 的直通,它允许它像defaultdict 一样运行,即创建一个带有关键字参数的字典。这对于将NestedDefaultDict 传递给json.load 很有用
  • 尝试my_dict = NestedDefaultDict(list) 返回TypeError - *args 是否旨在允许以这种方式定义叶子类型?
  • @AddisonKlinke 不,在这个实现中它不是。 default_factory 参数已被 NestedDefaultDict 类型占用。如果不构建更复杂的类,就没有简单的方法来检查当前节点是否为叶节点。但是,您可以编写 my_dict['a']['b'][0] 之类的内容来模拟具有列表类型的节点。
【解决方案6】:

我在这里基于 Andrew 的answer。 如果您希望将数据从 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

【讨论】:

    【解决方案7】:

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

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

    【讨论】:

      【解决方案8】:

      @nucklehead 的 response 也可以扩展为处理 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)
      

      【讨论】:

        【解决方案9】:

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

        (来自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
        

        【讨论】:

          【解决方案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))
          

          【讨论】:

            猜你喜欢
            • 2021-11-28
            • 2021-12-01
            • 2022-01-12
            • 2020-08-02
            • 2022-10-06
            • 1970-01-01
            相关资源
            最近更新 更多