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

如何通过递归用.拼接字符串变量?嵌套字典扁平化问题

递归实现嵌套字典的扁平化(点号拼接键名)

问题场景

我写了一个用来扁平化嵌套字典的Python函数flat:

def flat(errors: dict) -> dict:
    result = {}
    def inner(errors, key=None):
        if type(errors) is dict:
            for k, v in errors.items():
                inner(v, k)
        elif type(errors) is list:
            for obj in errors:
                inner(obj)
        else:
            result.update({key: errors})
    inner(errors)
    return result

测试数据如下:

errors = {
    "last_name": "Имя должно состоять из букв",
    "birth_place": {
        "address": {
            "parts": [
                {
                    "0": {
                        "id": "Неверный идентификатор",
                    },
                },
                {
                    "1": {
                        "id": "Неверный идентификатор",
                    },
                },
            ],
        },
    },
    "groups": [
        {
            "1": "Группа workers не существует",
        },
    ],
}

我期望得到的输出是:

{
    "last_name": "Имя должно состоять из букв",
    "birth_place.address.parts.0.id": "Неверный идентификатор",
    "birth_place.address.parts.1.id": "Неверный идентификатор",
    "groups.1": "Группа workers не существует",
}

但实际输出却是:

{'last_name': 'Имя должно состоять из букв', 'id': 'Неверный идентификатор', '1': 'Группа workers не существует'}

请问怎么通过递归实现用点号拼接完整的键路径?我对递归思维有点困惑。


问题分析

原函数的核心问题是:递归时只传递了当前层级的单个键,没有把之前层级的键路径传递下去。导致遇到叶子节点时,只能拿到最后一级的键名,重复的键会直接覆盖,最终丢失了完整的嵌套路径。

修正后的递归实现

我们需要在递归过程中维护当前已经拼接好的路径前缀,每进入一层嵌套(字典或列表),就把当前节点的标识(字典的键/列表的索引)拼接到路径上,直到遇到叶子节点时,用完整路径作为键存入结果:

def flat(errors: dict) -> dict:
    result = {}
    
    def inner(data, current_path=""):
        # 处理字典类型:遍历键值对,拼接路径
        if isinstance(data, dict):
            for k, v in data.items():
                # 如果当前路径为空,直接用当前键;否则用点号拼接
                new_path = f"{current_path}.{k}" if current_path else k
                inner(v, new_path)
        # 处理列表类型:遍历元素,用索引作为路径的一部分
        elif isinstance(data, list):
            for idx, item in enumerate(data):
                new_path = f"{current_path}.{idx}" if current_path else str(idx)
                inner(item, new_path)
        # 处理叶子节点:将完整路径和值存入结果
        else:
            result[current_path] = data
    
    inner(errors)
    return result

递归思路解释

递归的关键在于每一层调用都携带当前的上下文信息(这里就是已构建的路径),具体逻辑:

  • 初始调用时,current_path为空字符串,代表还没有进入任何嵌套层级。
  • 遇到字典:遍历每个键,把当前键拼接到已有路径后面(用点号分隔),然后递归处理对应的值。
  • 遇到列表:遍历每个元素,把元素的索引转为字符串拼接到已有路径后面,递归处理元素内容。
  • 遇到非字典非列表的叶子值:把当前的完整路径作为键,叶子值作为值存入结果字典。

这样就能保证每个叶子节点都能对应到从根节点到它的完整路径,不会出现键覆盖的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:35:34