
Pydantic 默认无法反序列化抽象基类字段,需通过 Union 类型联合 + discriminator 字段显式声明具体子类,才能正确实现多态成员的序列化与反序列化。
pydantic 默认无法反序列化抽象基类字段,需通过 `union` 类型联合 + `discriminator` 字段显式声明具体子类,才能正确实现多态成员的序列化与反序列化。
在构建类型安全的配置模型或领域对象时,常需支持多态结构(例如一个容器类持有一个可变类型的组件)。但 Pydantic 原生不支持直接对抽象基类(ABC)字段进行反序列化——因为 JSON 解析时无法凭空实例化抽象类。解决方案是放弃直接使用抽象类作为字段类型,转而采用显式的联合类型(Union)配合鉴别器(discriminator)字段,让 Pydantic 能根据 JSON 中的标识字段自动选择对应的具体子类。
核心步骤如下:
-
为每个具体子类定义统一的鉴别器字段(如
type: Literal["actual"]),该字段值必须唯一且不可变; -
在父类中声明该字段为
str(或Literal的超集),确保基类能被继承而不报错; -
字段类型声明使用
Union[ConcreteA, ConcreteB, ...],而非抽象基类; -
Pydantic 会自动识别
discriminator字段并路由到对应子类(无需额外配置,只要字段名一致且值唯一即可)。
以下是完整可运行示例:
from abc import ABC, abstractmethod
from pydantic import BaseModel, TypeAdapter, Field
from typing import Literal, Union
class I_Widget(ABC, BaseModel):
type: str # 抽象基类中声明 discriminator 字段(宽松类型)
@abstractmethod
def do_stuff(self) -> None:
...
class ActualWidget(I_Widget):
type: Literal["actual"] = "actual" # 具体子类固定值,作为 discriminator
value: int
def do_stuff(self) -> None:
print("Doing stuff")
class MockWidget(I_Widget):
type: Literal["mock"] = "mock"
name: str = "default"
def do_stuff(self) -> None:
print("Mocking stuff")
class WidgetHolder(BaseModel):
widget: Union[ActualWidget, MockWidget] # ✅ 显式列出所有可能子类
# 使用示例
if __name__ == "__main__":
# 序列化
wh1 = WidgetHolder(widget=ActualWidget(value=42))
json_str = wh1.model_dump_json()
print("Serialized:", json_str)
# 输出: {"widget": {"type": "actual", "value": 42}}
# 反序列化(自动识别 type="actual" → 实例化 ActualWidget)
wh2 = TypeAdapter(WidgetHolder).validate_json(json_str)
assert isinstance(wh2.widget, ActualWidget)
assert wh2.widget.value == 42
wh2.widget.do_stuff() # 正确调用子类方法⚠️ 注意事项:
-
discriminator字段名必须在所有子类中保持一致(如本例中的type),且值需为Literal类型以保证静态可推导; -
Union中必须穷举所有可能的子类,遗漏会导致反序列化失败; - 不要继承
ABC同时又继承BaseModel(虽语法允许,但易引发元类冲突);推荐将抽象逻辑抽离为纯ABC,再让BaseModel子类实现它(更清晰); - Pydantic v2 推荐使用
model_dump_json()和model_validate_json()替代已弃用的.json()和TypeAdapter(...).validate_json()(后者仍可用,但前者语义更明确)。
此方案兼顾类型安全性、运行时正确性与开发者体验,是 Pydantic 处理多态成员的标准实践。

















