You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何遍历Python图结构字典并按不同深度获取叶子节点,同时实现并行处理?

刚好我之前处理过类似的图遍历和并行任务,咱们一步步来解决你的问题:

1. 遍历图结构字典,按深度获取叶子节点

首先明确:你的图结构里,叶子节点就是children为空列表的节点对吧?我写两个方法,递归和迭代的,分别适合不同场景:

递归法(代码简洁,适合层级不深的图)

这个函数会返回一个字典,key是节点深度(根节点算0层),value对应深度的所有叶子节点的Node值:

def get_all_leaf_nodes(graph, current_depth=0, depth_map=None):
    if depth_map is None:
        depth_map = {}
    # 判断当前节点是否是叶子
    if not graph['children']:
        if current_depth not in depth_map:
            depth_map[current_depth] = []
        depth_map[current_depth].append(graph['Node'])
        return depth_map
    # 递归遍历子节点,深度+1
    for child in graph['children']:
        get_all_leaf_nodes(child, current_depth + 1, depth_map)
    return depth_map

用你的示例图测试:

example_graph = {'Node': [0, 0, 0], 'children': [{'Node': [1, 0, 0], 'children': [{'Node': [2, 0, 0], 'children': []}, {'Node': [1, 1, 0], 'children': []}, {'Node': [1, 0, 1], 'children': []}]}, {'Node': [0, 1, 0], 'children': [{'Node': [1, 1, 0], 'children': []}, {'Node': [0, 2, 0], 'children': []}, {'Node': [0, 1, 1], 'children': []}]}, {'Node': [0, 0, 1], 'children': [{'Node': [1, 0, 1], 'children': []}, {'Node': [0, 1, 1], 'children': []}, {'Node': [0, 0, 2], 'children': []}]}]}

depth_map = get_all_leaf_nodes(example_graph)
print(depth_map[2])  # 输出所有深度为2的叶子节点:[[2,0,0], [1,1,0], [1,0,1], ...]

迭代法(适合超深层级的图,避免递归栈溢出)

用栈模拟递归,逻辑和递归一致,但更稳定:

def get_leaf_nodes_by_depth_iterative(graph, target_depth=None):
    stack = [(graph, 0)]
    depth_map = {}
    while stack:
        node, depth = stack.pop()
        if not node['children']:
            if depth not in depth_map:
                depth_map[depth] = []
            depth_map[depth].append(node['Node'])
            continue
        # 反转子节点是为了保持和递归一样的遍历顺序,不需要的话可以去掉reverse
        for child in reversed(node['children']):
            stack.append((child, depth + 1))
    # 如果指定了目标深度,直接返回该深度的叶子,否则返回所有
    return depth_map.get(target_depth, []) if target_depth else depth_map
2. 并行处理叶子节点(用multiprocessing或joblib)

多进程的核心注意点:进程间内存隔离,所以不能直接在子进程里修改原字典,最好是把叶子节点的数据传递给子进程,处理完返回新的节点结构,再替换回原图。

先写你的添加子节点函数(这里模拟一个示例):

def add_child_to_node(node_data):
    # 这里写你实际的添加子节点逻辑,比如生成新的子节点
    new_child = {'Node': [x + 1 for x in node_data], 'children': []}
    # 返回更新后的节点(原叶子现在有了子节点)
    return {'Node': node_data, 'children': [new_child]}

用multiprocessing实现并行

import multiprocessing

def process_leaves_parallel(leaf_nodes):
    # 用CPU核心数创建进程池,也可以手动指定processes=4这种
    with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
        # map函数自动把每个叶子节点分配给子进程处理
        updated_nodes = pool.map(add_child_to_node, leaf_nodes)
    return updated_nodes

用joblib实现并行(更简洁)

joblib对numpy数组、字典这类数据的处理更友好,代码更短:

from joblib import Parallel, delayed

def process_leaves_joblib(leaf_nodes):
    # n_jobs=-1表示用所有CPU核心
    updated_nodes = Parallel(n_jobs=-1)(
        delayed(add_child_to_node)(node) for node in leaf_nodes
    )
    return updated_nodes

把处理后的节点替换回原图

最后需要一个辅助函数,把更新后的叶子节点替换到原结构图里:

def replace_leaf_node(graph, target_node_data, new_node):
    if not graph['children']:
        if graph['Node'] == target_node_data:
            return new_node
        return graph
    # 递归遍历子节点,替换目标节点
    new_children = [replace_leaf_node(child, target_node_data, new_node) for child in graph['children']]
    graph['children'] = new_children
    return graph

# 调用示例
leaf_nodes_data = get_leaf_nodes_by_depth_iterative(example_graph, target_depth=2)
updated_leaves = process_leaves_parallel(leaf_nodes_data)

# 逐个替换原叶子节点
for original_data, updated_node in zip(leaf_nodes_data, updated_leaves):
    example_graph = replace_leaf_node(example_graph, original_data, updated_node)
3. 遍历普通Python字典获取叶子节点

普通字典的叶子节点定义:值不是非空字典(或按需扩展到列表等容器)的键值对。这里写一个通用的遍历函数,能返回叶子节点的路径和值:

def get_plain_dict_leaves(d, parent_key='', sep='.'):
    leaves = []
    for k, v in d.items():
        # 生成当前节点的路径键,比如children[0].Node
        current_key = f"{parent_key}{sep}{k}" if parent_key else k
        # 如果是不为空的字典,继续递归
        if isinstance(v, dict) and v:
            leaves.extend(get_plain_dict_leaves(v, current_key, sep))
        # 如果是字典列表,遍历每个元素(可选,根据你的需求调整)
        elif isinstance(v, list) and all(isinstance(item, dict) for item in v):
            for i, item in enumerate(v):
                list_key = f"{current_key}[{i}]"
                leaves.extend(get_plain_dict_leaves(item, list_key, sep))
        # 否则就是叶子节点
        else:
            leaves.append((current_key, v))
    return leaves

用你的示例图测试,会返回类似这样的结果:

[('Node', [0, 0, 0]), ('children[0].Node', [1, 0, 0]), ('children[0].children[0].Node', [2, 0, 0]), ('children[0].children[0].children', []), ...]

如果不需要处理列表,直接把列表当作叶子值,可以去掉列表的判断分支,简化函数。


内容的提问来源于stack exchange,提问作者Nicola Alimonda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:37:47