Pydantic - 从 YAML 配置文件中解析对象列表

问题描述 投票:0回答:2

我想从 YAML 文件中读取对象列表:

- entry1:
   attribute: "Test1"
   amount: 1
   price: 123.45
- entry2:
   attribute: "Test1"
   amount: 10
   price: 56.78

对于这个数据结构,我创建了三个嵌套模型,如下所示:

# Models
class EntryValues(BaseModel):
    attribute: str
    amount: int
    price: float

class Entry(BaseModel):
    entry1: EntryValues
    entry2: EntryValues
class Config(BaseModel):
    __root__: list[Entry]

我读取 YAML 配置文件的代码如下所示:

# get YAML config path
def get_cfg_path() -> Path:
    return CWD

# read YAML file
def read_cfg(file_name: str, file_path: Path = None) -> YAML:
    if not file_path:
        file_path = get_cfg_path()

    if file_path:
        try:
            file = open(file_path / file_name, "r")
        except Exception as e:
            print(f"open file {file_name} failed", e)
            sys.exit(1)
        else:
            return load(file.read())
    else:
        raise Exception(f"Config file {file_name} not found!")

现在我想将 YAML 的值解压到我的模型中。为此,我尝试使用

**
运算符来解压这些值。我想我在这里又错过了一个循环,但我无法让它工作。

# Unpack and create config file
def create_cfg(file_name: str = None) -> Config:
    config_file = read_cfg(file_name=file_name)
    _config = Config(**config_file.data)
    return _config

我将不胜感激任何帮助。

更新

所以我在没有使用 YAML 文件的情况下稍微尝试了一下我的模型结构。我不太明白为什么下面会抛出

ValidationError
:

考虑以下条目列表(这与我从 YAML 文件收到的数据结构相同):

entries = [
    {'entry1': {'attribute': 'Test1', 'amount': 1, 'price': 123.45}}, 
    {'entry2': {'attribute': 'Test2', 'amount': 10, 'price': 56.78}}
]

如果我运行以下简单循环,那么 Pydantic 会抛出一个

ValidationError
:

for entry in entries:
    Entry(**entry)

错误:

ValidationError: 1 validation error for Entry
entry2
  field required (type=value_error.missing)

但是,如果列表仅包含一个条目字典,则它可以工作:

class Entry(BaseModel):
    entry1: EntryValues
    #entry2: EntryValues

entries = [
    {'entry1': {'attribute': 'Test1', 'amount': 1, 'price': 123.45}}
]

for entry in entries:
    Entry(**entry)

有人可以解释一下这个或者我在这里做错了什么吗?

python yaml pydantic
2个回答
1
投票

在您的更新中,第二种情况有效但第一种情况无效的原因是解包运算符(

**
)采用包含所有必要键的单个字典对象。在第一种情况下,您有一本包含所有必要信息的字典;在第二个中,它分布在两个字典中,并且它们无法一起解压。一种可能的解决方法是将它们合并到一个字典中。但据我了解,更好的解决方案是通过删除每行中的前两个字符来更改 YAML 以首先提供此功能:

entry1:
 attribute: "Test1"
 amount: 1
 price: 123.45
entry2:
 attribute: "Test1"
 amount: 10
 price: 56.78

然后:

_config = Config(__root__=[Entry(**entries)])

原答案:

您的代码存在许多问题,但我认为您想要做的是将 YAML 解析为字典并从每个项目实例化一个

EntryValues
。看起来像这样:

from pydantic import BaseModel
from pathlib import Path
from typing import List

import yaml


def create_cfg(file_name: str = None) -> Config:
    config_file = read_cfg(file_name=file_name)
    entries = yaml.safe_load(config_file)
    _config = [
        EntryValues(**di[name]) for di, name in zip(entries, ["entry1", "entry2"])
    ]
    return _config

0
投票

由于您正在尝试解析配置,因此您也可以考虑使用 pydantic-settings 模块而不仅仅是 pydantic。 但是,如果您的所有配置都来自 YAML,则这没有多大意义。

© www.soinside.com 2019 - 2024. All rights reserved.