有没有办法让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? 不同,后者是如何执行两级默认指令。
我可能最终会使用束模式,但当我意识到我不知道该怎么做时,它让我感兴趣。
答案 0 :(得分:132)
对于任意数量的级别:
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 :(得分:124)
此处的其他答案告诉您如何创建一个包含“无限多”defaultdict
的{{1}},但是它们无法解决我认为您最初需要的问题,即只需拥有一个两个深度的默认。
您可能一直在寻找:
defaultdict
您可能更喜欢这种结构的原因是:
defaultdict(lambda: defaultdict(dict))
的“叶子”不是字典,例如:defaultdict
或defaultdict(lambda: defaultdict(list))
答案 2 :(得分:41)
这样做有一个很好的技巧:
tree = lambda: defaultdict(tree)
然后,您可以使用x
创建x = tree()
。
答案 3 :(得分:18)
与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()
答案 4 :(得分:1)
我在这里基于安德鲁的answer。 如果要从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
答案 5 :(得分:0)
我还将提出更多OOP样式的实现,该实现支持无限嵌套以及格式正确的repr
。
class NestedDefaultDict(defaultdict):
def __init__(self):
super(NestedDefaultDict, self).__init__(NestedDefaultDict)
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 :(得分: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, {})
答案 7 :(得分:0)
@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)
答案 8 :(得分:0)
这是一个用于任意嵌套深度的任意基本 defaultdict 的函数。
(来自 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