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

如何基于Pydantic嵌套鉴别器优雅应用子列表约束?

基于Pydantic嵌套鉴别联合类型的子列表元素数量约束方案

问题背景

需要在Pydantic的嵌套鉴别联合类型列表中,对不同类型的元素分别应用数量约束:要求Cat类型元素至少1个(MinLen(1)),Dog类型元素最多2个(MaxLen(2)),且希望无需编写自定义验证器实现。

原模型代码如下:

from typing import Literal, Union
from typing_extensions import Annotated
from pydantic import BaseModel, Field, ValidationError


class BlackCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['black']
    black_name: str


class WhiteCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['white']
    white_name: str


Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]


class Dog(BaseModel):
    pet_type: Literal['dog']
    name: str


Pet = Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]


class PetsModel(BaseModel):
    pets: list[Pet]  # list of pet

已知可以对整个列表直接加全局约束,但无法区分类型:

from annotated_types import MaxLen, MinLen

class PetsModel(BaseModel):
    pets: Annotated[list[Pet], MinLen(1), MaxLen(5)]  # 全局约束,无法区分Cat/Dog

优雅解决方案(无需自定义验证器)

可以利用Pydantic的**计算字段(Computed Fields)**结合Annotated约束实现,通过计算字段分别统计Cat和Dog的数量,再对这些统计字段应用对应的约束:

from typing import Literal, Union, List
from typing_extensions import Annotated
from pydantic import BaseModel, Field, ValidationError, computed_field
from annotated_types import MaxLen, MinLen


class BlackCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['black']
    black_name: str


class WhiteCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['white']
    white_name: str


Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]


class Dog(BaseModel):
    pet_type: Literal['dog']
    name: str


Pet = Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]


class PetsModel(BaseModel):
    pets: list[Pet]
    
    @computed_field(return_type=Annotated[List[Cat], MinLen(1)])
    def cats(self) -> List[Cat]:
        return [pet for pet in self.pets if isinstance(pet, (BlackCat, WhiteCat))]
    
    @computed_field(return_type=Annotated[List[Dog], MaxLen(2)])
    def dogs(self) -> List[Dog]:
        return [pet for pet in self.pets if isinstance(pet, Dog)]

验证示例

  • 符合约束的输入:
valid_data = {
    "pets": [
        {"pet_type": "cat", "color": "black", "black_name": "black_cat_name"},
        {"pet_type": "dog", "name": "dog_name"},
    ]
}
model = PetsModel.model_validate(valid_data)
print(model.cats)  # 输出: [BlackCat(pet_type='cat', color='black', black_name='black_cat_name')]
print(model.dogs)  # 输出: [Dog(pet_type='dog', name='dog_name')]
  • 违反Cat数量约束(无Cat):
invalid_data_no_cat = {
    "pets": [
        {"pet_type": "dog", "name": "dog_name"},
    ]
}
try:
    PetsModel.model_validate(invalid_data_no_cat)
except ValidationError as e:
    print(e)
    # 提示 cats 字段长度至少为1
  • 违反Dog数量约束(超过2只Dog):
invalid_data_too_many_dogs = {
    "pets": [
        {"pet_type": "cat", "color": "black", "black_name": "black_cat_name"},
        {"pet_type": "dog", "name": "dog1"},
        {"pet_type": "dog", "name": "dog2"},
        {"pet_type": "dog", "name": "dog3"},
    ]
}
try:
    PetsModel.model_validate(invalid_data_too_many_dogs)
except ValidationError as e:
    print(e)
    # 提示 dogs 字段长度最大为2

方案说明

  • 利用@computed_field自动统计对应类型的元素列表,无需手动编写验证逻辑
  • 对计算字段的返回类型用Annotated添加MinLen/MaxLen约束,Pydantic会自动验证统计结果
  • 计算字段默认不会出现在模型的序列化输出中,若需调整可通过serialization_alias参数配置

内容的提问来源于stack exchange,提问作者MOHAMMED BILAL ANSARI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 19:47:47