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

Pydantic处理Union联合类型时匹配错误的解决方案咨询

问题原因

你遇到的两个问题本质都是Pydantic默认的Union类型匹配规则导致的:

  • Pydantic默认按Union定义的顺序从左到右尝试匹配类型
  • 默认开启隐式类型转换,且默认忽略模型未定义的额外字段,只要输入能满足左侧模型的字段要求(即使有额外字段、或者字段值可以隐式转换为目标类型),就会直接匹配左侧类型,不会继续尝试右侧的类型。

场景1:两个模型存在不同字段(编辑1的断言失败问题)

解法1:给模型添加禁止额外字段的配置

给所有Union涉及的模型添加extra = "forbid"的配置,匹配时如果输入包含当前模型未定义的字段,就会匹配失败,自动尝试Union的下一个类型:

class RequestPayloadPositionsParams(BaseModel):
    account: str = Field(default="COMBINED ACCOUNT")
    fields: List[str] = Field(default=["QUANTITY", "OPEN_PRICE", "OPEN_COST"])
    class Config:
        extra = "forbid" # 新增配置

class RequestPayloadOrdersParams(BaseModel):
    account: str = Field(default="COMBINED ACCOUNT")
    types: List[str] = Field(default=["WORKING", "FILLED", "CANCELED"])
    class Config:
        extra = "forbid" # 新增配置

修改后第二个断言即可正常通过。

解法2:使用判别器(推荐,性能更高)

如果模型有可以用来区分类型的固定字段(比如你示例里的header.service),可以用判别器明确指定匹配规则,无需逐个尝试类型:

Pydantic V2 示例:

from typing import Annotated, Union
from pydantic import Discriminator

def get_service_discriminator(v):
    return v["header"]["service"] if isinstance(v, dict) else v.header.service

PayloadItem = Annotated[Union[RequestPayloadLogin, RequestPayloadService], Discriminator(get_service_discriminator)]

class RequestPayload(BaseModel):
    payload: List[PayloadItem] = Field(...)

场景2:两个模型字段完全一致仅字段类型不同(编辑2的类型转换问题)

解法1:开启严格类型匹配+智能Union模式

  1. 用严格类型StrictStr、StrictFloat代替普通的str、float,禁止隐式类型转换
  2. 开启智能Union模式,让Pydantic匹配最符合的类型而非直接取第一个可转换的类型

Pydantic V1 配置:

from pydantic import StrictStr, StrictFloat

class ResponseReplacePatchStr(BaseModel):
    op: str = Field(default="replace")
    path: str = Field(...)
    value: StrictStr = Field(...) # 替换为StrictStr
    class Config:
        extra = "forbid"
        smart_union = True # 开启智能Union

class ResponseReplacePatchFloat(BaseModel):
    op: str = Field(default="replace")
    path: str = Field(...)
    value: StrictFloat = Field(...) # 替换为StrictFloat
    class Config:
        extra = "forbid"
        smart_union = True # 开启智能Union

Pydantic V2 配置:

from pydantic import StrictStr, StrictFloat

class ResponseReplacePatchStr(BaseModel):
    op: str = Field(default="replace")
    path: str = Field(...)
    value: StrictStr = Field(...)
    model_config = {"extra": "forbid", "union_mode": "smart"}

class ResponseReplacePatchFloat(BaseModel):
    op: str = Field(default="replace")
    path: str = Field(...)
    value: StrictFloat = Field(...)
    model_config = {"extra": "forbid", "union_mode": "smart"}

修改后输入为字符串时会匹配ResponseReplacePatchStr,输入为数字时会匹配ResponseReplacePatchFloat,不会出现强制转换的问题。

解法2:调整Union顺序(临时应急)

如果不想修改模型配置,也可以根据业务场景把匹配优先级更高的类型放在Union左侧,比如大部分场景下value是数字的话,就把ResponseReplacePatchFloat放在Union前面。


内容的提问来源于stack exchange,提问作者Martin Fischer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 12:06:03