Python Pydantic解析YAML配置对象列表ValidationError报错排查
错误原因
核心矛盾是Pydantic模型定义的字段约束和实际YAML解析出的数据结构完全不匹配:
你定义的Entry模型把entry1和entry2都设为了必填字段,意味着每一个Entry实例必须同时包含这两个键。但你提供的YAML解析后得到的是两个独立的单键字典:第一个字典只有entry1键,第二个字典只有entry2键,循环实例化时,处理第一个字典会报缺entry2,处理第二个会报缺entry1,自然抛出校验错误。
你之前注释掉entry2字段后单条数据能跑通,只是刚好测试数据和简化后的模型结构碰巧匹配,不代表原有逻辑正确。
现存问题
- 数据模型映射错误:没有对齐实际YAML的层级结构,把分属不同列表项的互斥键,错误定义成了同一对象下的并列必填字段。
- YAML读取逻辑存在隐患:直接调用
open()没有用with上下文管理器,文件读取完成后不会自动关闭,会造成文件句柄泄漏;另外直接使用yaml.load()没有指定安全加载模式,存在执行恶意代码的风险。 - 解析逻辑冗余:Pydantic本身支持直接传入嵌套结构完成批量校验,不需要手动写循环逐层级解包。
正确实现方案
根据你实际的业务需求,二选一调整即可:
方案1:保留现有YAML结构(entry1、entry2为独立列表项)
如果你的业务场景就是需要在列表中存放任意数量的、键名不固定的条目(比如后续可能加entry3、entry4),直接调整模型定义,用字典类型接收动态键即可,不需要单独定义Entry类:
from pydantic import BaseModel from typing import Dict, List from pathlib import Path import yaml import sys # 条目值模型保持不变 class EntryValues(BaseModel): attribute: str amount: int price: float # 根模型直接接收「元素为单键字典的列表」 class Config(BaseModel): __root__: List[Dict[str, EntryValues]] # 路径配置示例,替换成你实际的CWD值即可 CWD = Path(__file__).parent def get_cfg_path() -> Path: return CWD def read_cfg(file_name: str, file_path: Path = None): if not file_path: file_path = get_cfg_path() target_file = file_path / file_name if not target_file.exists(): raise Exception(f"Config file {file_name} not found!") try: # 用with上下文自动管理文件关闭,用safe_load避免安全风险 with open(target_file, "r", encoding="utf-8") as f: return yaml.safe_load(f) except Exception as e: print(f"read file {file_name} failed: {e}") sys.exit(1) # 直接解析,不需要手动循环 if __name__ == "__main__": yaml_content = read_cfg("your_config.yaml") config = Config.parse_obj(yaml_content) # 读取数据示例 for entry in config.__root__: for entry_name, entry_detail in entry.items(): print(f"条目名:{entry_name}") print(f"属性:{entry_detail.attribute}, 数量:{entry_detail.amount}, 价格:{entry_detail.price}")
方案2:保留原有模型定义(entry1、entry2为同一项下的并列属性)
如果你本来的设计是每个配置项同时包含entry1和entry2两个条目,那不需要改模型,只需要修正YAML的缩进结构,把两个键放到同一个列表项里即可:
# 修正后的YAML结构 - entry1: attribute: "Test1" amount: 1 price: 123.45 entry2: attribute: "Test2" amount: 10 price: 56.78
读取和解析逻辑和方案1一致,直接调用Config.parse_obj(yaml_content)即可完成自动校验和实例化,不需要手动写循环处理。
内容的提问来源于stack exchange,提问作者arodin
相关产品推荐
相关产品推荐

