pydantic:当父类键入类字段时,反序列化错误



下面的代码尝试将C的实例序列化为dict,然后再次反序列化回C。由于data的类型信息在dict()的序列化过程中丢失,data实例被解释为A的实例,而不是B(我的期望)。

我希望c_againc相互匹配。我该怎么做呢?

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

基于@bc291的评论:

作为起点,您需要将data类型提示为几种可能类型的Union,在您的示例中是data: Union[A, B]。但这还不够:如果您只这样做,pydantic将尝试将您的数据匹配到它可以完成的第一个类型,因此它仍然将c_again.data实例化为A(a=1),忽略b键/属性。您可以通过在联合中首先提示B来解决这个问题,就像Union[B, A]一样,这是可行的,但它既不优雅也不健壮,特别是如果您的实际用例涉及许多子类,如您所说。

因此,您还需要将Config添加到A,这将使pydantic在b存在时拒绝实例化它,因此它将跳过它并实例化一个匹配所有键/属性的Union类(在本例中为B)。注意,A的子类将继承此行为。

from typing import Union
from pydantic import BaseModel

class A(BaseModel):
a: int
class Config:
extra = 'forbid'

class B(A):
b: float

class C(BaseModel):
data: Union[A, B]

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=B(a=1, b=0.2)
# no assertion error