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

如何利用Pydantic特性从嵌套字典提取架构至指定子模型?

解决方案:用Pydantic特性替代__post_init__手动赋值

首先得提个小问题:你的输入数据里databases是字典结构(键为service_db和other_db),但原代码里把databases定义成了List类型,这会导致解析失败,所以第一步要修正类型定义为Dict[str, Union[ServiceDatabase, OtherDatabase]],匹配输入数据的实际结构。

接下来,我们可以用Pydantic的特性实现你想要的「自动从指定字段提取子模型」的效果,不需要再依赖__post_init__手动赋值。下面分两种常用场景给出方案:

方案一:用Pydantic v2的计算字段(推荐)

如果你的项目使用Pydantic v2,@computed_field是最优雅的解决方案,它会自动在实例化时计算出目标属性:

from dataclasses import asdict
from typing import Dict, Union
from pydantic.dataclasses import dataclass
from pydantic import computed_field

@dataclass
class ServiceDatabase:
    connect_string: str

@dataclass
class OtherDatabase:
    connect_string: str
    service: str

@dataclass
class PydConfigurator:
    databases: Dict[str, Union[ServiceDatabase, OtherDatabase]]

    @computed_field
    def service_db(self) -> ServiceDatabase:
        # 直接从databases字典中提取对应键的配置,Pydantic会自动做类型校验
        return self.databases["service_db"]

    @computed_field
    def other_db(self) -> OtherDatabase:
        return self.databases["other_db"]

data = {
    "databases": {
        "service_db": {
            "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}"
        },
        "other_db": {
            "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}",
            "service": "Any"
        }
    }
}

if __name__ == '__main__':
    config = PydConfigurator(**data)
    assert asdict(config.service_db) == data['databases']['service_db']
    assert asdict(config.other_db) == data['databases']['other_db']
    print("所有断言通过!")

关键说明

  • @computed_field装饰器告诉Pydantic:这两个属性是基于已有字段计算得到的,不需要用户传入,会在实例化时自动生成。
  • 类型安全:Pydantic会自动校验提取的值是否符合目标类型(比如service_db必须是ServiceDatabase实例),如果类型不匹配会直接抛出验证错误,比手动赋值更可靠。

方案二:用Pydantic验证器(兼容v1)

如果你的项目还在使用Pydantic v1,或者更习惯用验证器的方式,可以通过@validator实现自动提取:

from dataclasses import asdict
from typing import Dict, Union
from pydantic.dataclasses import dataclass
from pydantic import validator

@dataclass
class ServiceDatabase:
    connect_string: str

@dataclass
class OtherDatabase:
    connect_string: str
    service: str

@dataclass
class PydConfigurator:
    databases: Dict[str, Union[ServiceDatabase, OtherDatabase]]
    service_db: ServiceDatabase = None
    other_db: OtherDatabase = None

    @validator("service_db", pre=True, always=True)
    def extract_service_db(cls, v, values):
        # pre=True:在其他验证逻辑前执行;always=True:即使没传入参数也会执行
        return values["databases"]["service_db"]

    @validator("other_db", pre=True, always=True)
    def extract_other_db(cls, v, values):
        return values["databases"]["other_db"]

data = {
    "databases": {
        "service_db": {
            "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}"
        },
        "other_db": {
            "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}",
            "service": "Any"
        }
    }
}

if __name__ == '__main__':
    config = PydConfigurator(**data)
    assert asdict(config.service_db) == data['databases']['service_db']
    assert asdict(config.other_db) == data['databases']['other_db']
    print("所有断言通过!")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:33:47