如何将嵌套的字典转换为defaultdict?

5

我该如何将嵌套字典转换为嵌套的defaultdict?

dic = {"a": {"aa": "xxx"}}
default = defaultdict(lambda: None, dic)
print(default["dummy_key"])  # return None
print(default["a"]["dummy_key"])  # KeyError
1个回答

13

你需要在嵌套字典中循环或递归,遍历其所有级别。

除非它可能非常深层(如数百个级别),或者非常宽而小的性能因素会产生巨大影响,否则递归在这里可能是最简单的方法:

def defaultify(d):
    if not isinstance(d, dict):
        return d
    return defaultdict(lambda: None, {k: defaultify(v) for k, v in d.items()})
如果您希望它适用于所有映射,而不仅仅是字典,您可以在isinstance检查中使用collections.abc.Mapping而不是dict
当然,这是假设您有一个纯嵌套的字典。如果您有一个从典型JSON响应解析出的东西,其中可能存在具有字典元素的列表值的字典,则还必须处理其他可能性:
def defaultify(d):
    if isinstance(d, dict):
        return defaultdict(lambda: None, {k: defaultify(v) for k, v in d.items()})
    elif isinstance(d, list):
        return [defaultify(e) for e in d]
    else:
        return d

如果这实际上是来自JSON的数据,最好在解析JSON时将您的defaultdict作为object_pairs_hook使用,而不是将其解析为字典,然后稍后再转换为defaultdict

文档中有一个使用OrderedDict替换dict的示例,但对我们来说不太适用——与OrderedDictdict不同,defaultdict不能仅将可迭代的键值对作为其唯一参数;首先需要默认值工厂函数。因此,我们可以使用functools.partial绑定它:

d = json.loads(jsonstring, object_hook_pairs=partial(defaultdict, lambda: None))
< hr >

等等。


谢谢,它运行得非常好。是的,我也有一个列表,你的建议对我非常有帮助。非常感谢你。 - Maiko Ohkawa
@MaikoOhkawa 如果你确实是从json.loads或类似的方法中获取这个数据,那么你可能想考虑一开始就将其构建为一个defaultdict,而不是先构建一个dict再进行转换。例如,json文档解释了如何使解码器对每个JSON对象使用OrderedDict而不是dict,你可以做同样的事情来使用defaultdict - abarnert
非常感谢。我尝试了,但是出现了一个错误。json.loads('{"a": {"aa": "xxx"}}', object_pairs_hook=collections.defaultdict) # TypeError: 第一个参数必须是可调用对象或空值也许我错了。 - Maiko Ohkawa
@MaikoOhkawa 你不能直接使用 defaultdict,因为它需要一个额外的第一个参数。但是你可以使用例如 functools.partial(defaultdict, lambda: None) 或者 lambda *args, **kw: defaultdict(lambda: None, *args, **kw)。我会将这个编辑到答案中。 - abarnert
2
这个完美地运行了,非常感谢!顺便说一下,我提交了一个编辑来更改你上一个例子中的 object_hook_pairsobject_pairs_hook,但是出现了错误并发现了拼写错误。 - bergonzzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接