【问题标题】:Traversing a nested dict-like structure, keeping track of each path from root to leaf (Python)遍历嵌套的类似字典的结构,跟踪从根到叶的每条路径(Python)
【发布时间】:2021-11-05 01:00:57
【问题描述】:

我正在尝试遍历具有任意深度嵌套叶子的对象,获取每个根节点到叶子的路径。这方面的一个例子可能是:

class Field (object):
    def __init__(self, name, subfields=None):
        self.name = name
        self.subfields = subfields


f1 = Field("username")
f2 = Field("ip_address")
f3 = Field("song_id")
f11 = Field("song_length")
f10 = Field("misc_attrs", subfields=[f3, f11])

f4 = Field("event_time")
f5 = Field("song_name", subfields=[f1,f2])
f6 = Field("user_song_upload", subfields=[f10])

input_list = [f4,f5,f6]

我的目标是生成列表output 的列表,其中output 中的每个列表代表从根节点到叶子的路径。所以在上面的例子中,我们会有以下输出:

[["event_time"],["song_name","username"],["song_name","ip_address"],["user_song_upload","misc_attrs","song_id"],["user_song_upload","misc_attrs","song_length"]]

我有一个可以返回所有叶节点的函数,但是我无法跟踪每个节点的完整路径。这是我目前所拥有的:

def get_leaf_paths(input_list, output = [], path=[]):
    for x in input_list:
        if not x.subfields:
            # we have a leaf
            path.append(x.name)
            output.append(path) 
            # next line clears the leaf from our last path
            path = path[:-1]

        else:
            # we don't have a leaf node, and need to iterate on the subfields
            path.append(x.name)
            get_leaf_paths(x.subfields, output, path)
    return output

但是,这会导致路径构建不正确(例如,两个相邻的顶级节点最终会出现在输出中的同一个内部列表中:

[['event_time'],
 ['song_name', 'username', 'user_song_upload', 'misc_attrs', 'song_id'],
 ['song_name', 'ip_address'],
 ['song_name', 'username', 'user_song_upload', 'misc_attrs', 'song_id'],
 ['song_name', 'username', 'user_song_upload', 'misc_attrs', 'song_length']]

【问题讨论】:

  • 您的字典不是有效的字典,例如键“event_time”和“browser”没有值。
  • 谢谢 - 我正在修改它以明确我们在这里谈论的是更通用的对象,而不是字典。
  • 您的“更通用的对象”仍然不是有效的 Python。请提供一个具体有效的例子。如果您只提供一些抽象的伪对象,那么可以作为答案提供的只是您需要的一些描述 - 您已经给了我们。您的示例代码引用了一些 input_list,但您没有提供此类列表的示例。
  • 好的,我已经编辑了问题,所以我的对象是有效的 Python,并且提供的函数可以运行而不会出现错误。

标签: python object recursion path traversal


【解决方案1】:

您可以构建适当的基本案例来简化代码。请尝试以下操作:

def get_leaf_paths(lst):
    if not lst: # empty list or None
        return [[]]
    return [[f.name, *path] for f in lst for path in get_leaf_paths(f.subfields)]

print(get_leaf_paths(input_list))

在代码中,基本情况是“一个空列表或无”,即当subfields 被传递给函数但subfields 实际上是None。当函数在叶节点处调用时就是这种情况。

输出:

[['event_time'], ['song_name', 'username'], ['song_name', 'ip_address'], ['user_song_upload', 'misc_attrs', 'song_id'], ['user_song_upload', 'misc_attrs', 'song_length']]

【讨论】:

    【解决方案2】:

    如果您需要将其应用于非常深的结构并且递归(如@j1-lee 在他们非常干净的实现中建议)不是一种选择,则此方法可行:

    def get_leaf_paths(lst):
        stack, l, i = [], lst, 0
        while l and len(l) > i:
            if l[i].subfields:
                stack.append((l, i))
                l, i = l[i].subfields, 0
            else:
                yield [x[n].name for x, n in stack] + [l[i].name]
                i += 1
            if i >= len(l):
                l, i = stack.pop()
                i += 1
    
    
    print(list(get_leaf_paths(input_list)))
    

    结果:

    [['event_time'], ['song_name', 'username'], ['song_name', 'ip_address'], ['user_song_upload', 'misc_attrs', 'song_id'], ['user_song_upload', 'misc_attrs', 'song_length']]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-09
      • 2013-07-21
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 2021-03-19
      相关资源
      最近更新 更多