嵌套的defaultdict defaultdict


129

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

我希望能够做到:

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

因此,我可以做到x = defaultdict(defaultdict),但这仅是第二层:

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

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

请注意,这是在问如何执行无限级递归defaultdict,因此它与Python不同:defaultdict的defaultdict?,这是执行两级defaultdict的方法。

我可能最终会使用模式,但是当我意识到自己不知道该怎么做时,这引起了我的兴趣。



2
并非真的...在问题中添加了信息以说明原因。虽然这是一个有用的问题。
科利·布里格曼

Answers:


168

对于任意数量的级别:

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)

1
的确是一个完美的例子,谢谢。您能否将其扩展到数据从json加载到defaultdict的defaultdict的情况?
David Belohrad

4
一注。如果您试图在腌制lambda时使用此代码将无法正常工作。
Viacheslav Kondratiuk

166

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

您可能一直在寻找:

defaultdict(lambda: defaultdict(dict))

您可能更喜欢此构造的原因是:

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

3
defaultdict(lambda:defaultdict(list))正确的形式?
Yuvaraj Loganathan 2015年

糟糕,是lambda正确的格式-因为defaultdict(something)返回的是字典类的对象,但是defaultdict期望可调用!谢谢!
克里斯·W

4
这被标记为可能是另一个问题的重复...但这不是我的原始问题。我知道如何创建两个级别的defaultdict。我不知道如何使它递归。这个答案,其实,类似stackoverflow.com/questions/5029934/...
科利Brigman

Lambda方法的dict(result)
缺点之一

54

有一个不错的技巧:

tree = lambda: defaultdict(tree)

然后,您可以使用创建自己xx = tree()


22

与BrenBarn的解决方案类似,但是不包含tree两次变量名,因此即使更改了变量字典也可以使用:

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

然后,您可以创建的每个新xx = tree()


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

from collections import defaultdict

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

4
我将不得不考虑这一点(这要复杂一些)。但我认为您的意思是,如果x = tree(),但是后来有人来了,并且tree = None仍然有效,那将不会吗?
Corley Brigman 2013年

11

我还将提出更多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': {}}

1
干净!我添加了passthrough *args**kwargs允许它像一样运行defaultdict,即使用关键字参数创建字典。这种传递是非常有用的NestedDefaultDictjson.load
西普里安Tomoiagă

0

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

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

0

我在这里基于安德鲁的答案。如果要从json或现有字典将数据加载到嵌套程序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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.