【问题标题】:Is there a pythonic way to process tree-structured dict keys?有没有一种处理树形结构的字典键的pythonic方法?
【发布时间】:2016-07-20 19:59:45
【问题描述】:

我正在寻找一个 pythonic 成语将键列表和值转换为嵌套这些键的字典。例如:

dtree(["a", "b", "c"]) = 42
    or
dtree("a/b/c".split(sep='/')) = 42

将返回嵌套的字典:

{"a": {"b": {"c": 42}}}

这可用于将一组具有分层键的值转换为树:

dtree({
    "a/b/c": 10,
    "a/b/d": 20,
    "a/e": "foo",
    "a/f": False,
    "g": 30 })

would result in:

{   "a": {
        "b": {
            "c": 10,
            "d": 20 },
        "e": foo",
        "f": False },
    "g": 30 }

我可以编写一些 FORTRANish 代码来使用蛮力和多个循环进行转换,也许是 collections.defaultdict,但它似乎是一种具有拆分和连接、切片和理解的语言应该有一个原语来转换字符串列表 @ 987654325@ 到嵌套的字典键 ["a"]["b"]["c"]。在 dict 表达式字符串上不使用 eval 的最短方法是什么?

【问题讨论】:

    标签: python dictionary tree


    【解决方案1】:

    我正在寻找一个 pythonic 成语将键列表和值转换为嵌套这些键的字典。

    reduce(lambda v, k: {k: v}, reversed("a/b/c".split("/")), 42)
    

    这可用于将一组具有分层键的值转换为树

    def hdict(keys, value, sep="/"):
        return reduce(lambda v, k: {k: v}, reversed(keys.split(sep)), value)
    
    def merge_dict(trg, src):
        for k, v in src.items():
            if k in trg:
                merge_dict(trg[k], v)
            else:
                trg[k] = v
    
    def hdict_from_dict(src):
        result = {}
        for sub_hdict in map(lambda kv: hdict(*kv), src.items()):
            merge_dict(result, sub_hdict)
        return result
    
    data = {
        "a/b/c": 10,
        "a/b/d": 20,
        "a/e": "foo",
        "a/f": False,
        "g": 30 }
    
    print(hdict_from_dict(data))
    

    另一个使用collections.defaultdict的整体解决方案

    import collections
    
    def recursive_dict():
        return collections.defaultdict(recursive_dict)
    
    def dtree(inp):
        result = recursive_dict()
        for keys, value in zip(map(lambda s: s.split("/"), inp), inp.values()):
            reduce(lambda d, k: d[k], keys[:-1], result)[keys[-1]] = value
        return result
    
    import json
    print(json.dumps(dtree({
        "a/b/c": 10,
        "a/b/d": 20,
        "a/e": "foo",
        "a/f": False,
        "g": 30 }), indent=4))
    

    【讨论】:

    • 这真是一个很酷的答案!你让我谷歌解释了这个和其他类似的python内置函数:bogotobogo.com/python/python_fncs_map_filter_reduce.php
    • Reversed 可以改成[::-1],也许吧?
    • @AmitGold 没有理由这样做。事实上[::-1] 效率较低,因为它必须构建一个新列表,而reversed 只是一个迭代器。
    • @Bakuriu 我实际上并不知道reversed 是如何工作的,但在我看来list 的实现将更具体到列表本身,因此比reversed 更好应该处理任意可迭代的......(我认为)
    • @AmitGold 不,reversed 需要一个 sequence 并简单地调用 __getitem__,索引从最后一个开始,但它是惰性的,只需要恒定空间。使用[::-1] 将产生一个新的列表,使内存翻倍(至少在一段时间内,直到另一个列表被垃圾回收)。
    【解决方案2】:

    或者只是为了笑,因为reduce 是自切片面包以来最酷的东西,你可以通过使用两次来节省一个 SLOC :-)

    def dmerge(x, y):
        result = x.copy()
        k = next(iter(y))
        if k in x:
            result[k] = dmerge(x[k], y[k])
        else:
            result.update(y)
        return result
    
    def hdict(keys, value, sep="/"):
        return reduce(lambda v, k: {k: v}, reversed(keys.split(sep)), value)
    
    def hdict_from_dict(src):
        return reduce(lambda x, y: dmerge(x, y), [hdict(k, v) for k, v in src.items()])
    
    data = {
        "a/b/c": 10,
        "a/b/d": 20,
        "a/e": "foo",
        "a/f": False,
        "g": 30 }
    
    print("flat:", data)
    print("tree:", hdict_from_dict(data))
    

    【讨论】:

    • 我有一个类似的原型。最后,我不喜欢通过reduce 的管道副作用,而没有真正利用归约函数的返回值。然而,这不是可能的最短版本。你不需要复制x。只需修改x 并将其作为结果返回。创建 hdicts 并不重要。此外,您可以使用列表推导来解压映射对。这样您就可以在缩减函数中模式匹配ky[k]。精简版见this gist。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多