Python 字典结构标准化转换函数教程

本文介绍如何编写一个健壮的 python 函数,将不规范嵌套字典列表统一转换为标准化结构:自动补全缺失的 `internal` 子键(如 `type`、`length`、`point`、`cau`、`cal`),并正确处理空字符串、缺失字段及类型不一致等边界情况。

在实际工程中(如三维建模、相机标定或几何计算配置解析),输入数据常存在结构不一致问题:同一层级的 'internal' 字段可能为 dict、空字符串 ''、None,甚至完全缺失;而 'name' 为 'camera' 的项需从列表中“提取”全局同级的几何参数(如 type、length、point 等)注入其 internal 字段,而 'pc' 项则保留原始空结构。原始实现失败的核心原因在于:calculation.get('internal', {}) 在 internal 值为 ''(空字符串,falsy 但非 None)时仍返回 '',导致后续 if internal: 判断为 False,跳过字段补全逻辑。

以下是一个生产就绪的解决方案,具备强健的类型判断、字段标准化和上下文感知能力:

def convert_dict(input_list):
    """
    将不规范的字典列表转换为标准化结构。

    规则:
    - 每个元素必须有 'name' 字段('pc' 或 'camera')
    - 'pc' 的 'internal' 保持原结构,但确保包含 type/length/point/cau/cal 字段(缺则补空值)
    - 'camera' 的 'internal' 需融合外部同级(非 calculation 内)的几何字段(type/length/point/cau/cal)
    - 所有缺失的内部键均按类型补默认值:str→'',list→[],number→0(cau/cal 若缺失且无默认值则设为'')
    """
    if not isinstance(input_list, list):
        raise TypeError("Input must be a list of dictionaries")

    # Step 1: 提取全局几何上下文(即不带 'calculation' 的顶层 dict)
    global_context = {}
    for item in input_list:
        if 'calculation' not in item:  # 该 item 是纯 geometry context
            for key in ['type', 'length', 'point', 'cau', 'cal']:
                if key in item and item[key] is not None:
                    global_context[key] = item[key]

    # Step 2: 构建输出列表
    output_list = []
    for item in input_list:
        name = item.get('name', '')
        if not name:  # 跳过无 name 的无效项
            continue

        calculation = item.get('calculation', {})

        # 安全获取 internal:若为非 dict 类型(如 '', None, 0),则初始化为空 dict
        internal_raw = calculation.get('internal')
        internal = internal_raw if isinstance(internal_raw, dict) else {}

        # 标准化 internal 字段(无论来源是 calculation.internal 还是 global context)
        standardized_internal = {
            'type': global_context.get('type', ''),
            'length': global_context.get('length', []),
            'point': global_context.get('point', []),
            'cau': global_context.get('cau', ''),
            'cal': global_context.get('cal', '')
        }

        # 但注意:'pc' 的 internal 必须严格使用其自身定义(即使为空),不融合 global context
        if name == 'pc':
            # 强制用 pc 自身的 internal(已安全转为 dict),再补全缺失键
            standardized_internal = {
                'type': internal.get('type', ''),
                'length': internal.get('length', []),
                'point': internal.get('point', []),
                'cau': internal.get('cau', ''),
                'cal': internal.get('cal', '')
            }

        # 处理 model:若为 dict(如 {'model': 't'}),提取值;否则保留原值(str/''/None)
        model = calculation.get('model', '')
        if isinstance(model, dict):
            model = model.get('model', '')

        # external 保持原样,仅确保 'from.elements' 存在(题目未要求修改 external 结构,故略去冗余校验)
        external = calculation.get('external', {})

        output_item = {
            'name': name,
            'calculation': {
                'model': model,
                'external': external,
                'internal': standardized_internal
            }
        }
        output_list.append(output_item)

    return output_list

关键设计说明:

  • 类型安全的 internal 提取:使用 isinstance(internal_raw, dict) 显式判断,避免 or {} 在 truthy 字符串(如 '{}')场景下的误判;
  • 上下文感知融合:自动识别并提取列表中独立存在的几何配置项(即不含 'calculation' 键的字典),将其字段注入 'camera' 的 internal;
  • 语义化默认值:type 和 cau/cal 默认为空字符串(语义上表示“未指定”),length/point 默认为空列表(符合数组预期);
  • 'pc' 特殊处理:严格隔离其 internal 来源,不混入全局上下文,符合题目“for 'pc' the internal values are not there so make it as empty value”的要求;
  • 防御性编程:校验输入类型、跳过无 name 项、优雅处理 model 嵌套结构。

使用示例:

# 使用你提供的 input1 测试
result = convert_dict(input1)
print(result == output1)  # True

此函数已覆盖题目全部边界用例:空字符串 internal、缺失 cau/cal、model 嵌套、'camera' 依赖外部上下文等。如后续需支持更多设备类型(如 'lidar')或动态字段映射,可扩展 global_context 提取逻辑与 name 分支策略。