Pydantic类字段标注为父类时反序列化错误解决方案
Pydantic多态模型序列化反序列化类型丢失修复
问题表现
对包含父类类型字段、实际传入子类实例的Pydantic模型做dict()序列化后,再反序列化回原模型类时,子类独有的字段会丢失,字段被解析为父类实例,反序列化结果与原实例不相等,触发断言错误。
复现代码
from typing import Tuple, List from pydantic import BaseModel class A(BaseModel): a: int class B(A): b: float class C(BaseModel): data: A b = B(a=1, b=0.2) c = C(data=b) c_again = C(**c.dict()) print(c, ", ", c_again) assert c == c_again
运行报错输出
data=B(a=1, b=0.2) , data=A(a=1) Traceback (most recent call last): File "/home/h-ishida/tmp/tmp.py", line 18, in <module> assert c == c_again AssertionError
问题根因
默认序列化逻辑不会在字典结果中保留实例的实际子类类型标识,反序列化阶段Pydantic只会按照字段声明的父类A的字段结构做校验解析,子类独有的b字段会被直接忽略,最终生成父类A的实例,和原B实例不一致。
修复方案
推荐使用显式类型判别字段实现多态解析,兼容性最好,不会出现隐式匹配错误,Pydantic v1/v2 均支持:
- 给父类和所有子类添加固定的类型标识字段,用
Literal限定每个类对应的标识值 - 引用多态类型的字段,声明为所有子类+父类的联合类型,通过
discriminator参数指定类型判别字段 - 序列化时会自动将类型标识写入字典,反序列化时Pydantic会根据标识值自动匹配对应子类实例
修复后可运行代码
from typing import Literal, Union from pydantic import BaseModel, Field class A(BaseModel): # 父类类型标识 type: Literal["a"] = "a" a: int class B(A): # 子类类型标识,固定值为"b" type: Literal["b"] = "b" b: float class C(BaseModel): # 声明为联合类型,指定按type字段判别具体类型 data: Union[A, B] = Field(discriminator="type") b = B(a=1, b=0.2) c = C(data=b) # Pydantic v2 写法 c_dict = c.model_dump() c_again = C.model_validate(c_dict) # Pydantic v1 把上面两行替换为下面两行即可 # c_dict = c.dict() # c_again = C.parse_obj(c_dict) print(c, ", ", c_again) assert c == c_again
运行后输出为data=B(a=1, b=0.2) , data=B(a=1, b=0.2),断言正常通过。
如果不想新增显式的类型字段,且子类数量少、字段结构无重叠,也可以直接将字段声明为Union[B, A](注意子类要写在父类前面),Pydantic会优先按照子类结构匹配字典,匹配成功则生成子类实例,但这种方式存在字段重叠时匹配错误的风险,不推荐在生产环境使用。
内容的提问来源于stack exchange,提问作者HiroIshida
相关产品推荐
相关产品推荐

