pydantic:当类字段由父类键入时,不正确

发布于 2025-02-06 09:34:16 字数 791 浏览 1 评论 0原文

以下代码尝试序列化c的实例,然后再次对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

The following code try to serialize the instance of C to dict and then deserialize back to C again. Because type information of data is lost in serialization process in dict(), the data instance is interpreted as an instance of A instead of B (my expectation).

I want c_again and c match each other. How can I do this?

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

output

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

最初的梦 2025-02-13 09:34:16

在 @bc291的评论上进行构建:

您需要的起点是键入data作为几种可能类型的union,在您的示例中数据:联合[A,B]。但这还不够:如果您只这样做,Pydantic将尝试将您的数据与它可以完成的第一个类型匹配,因此它仍然可以实例化c_again.data as a( a = 1),忽略b键/属性。您可以首先在联合中使用类型b来解决此问题,例如union [b,a],它既行之有效,又不优雅,也不会强大,尤其是在您的实际用例涉及您所说的许多子类。

因此,您还需要将config添加到a,如果存在b,它将使Pydantic拒绝实例化,因此它将跳过它并实例化Union的类,该类改为匹配所有密钥/属性(在这种情况下,b)。请注意,旨意继承这种行为的sublasses。

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

Building up on @bc291 's comment:

What you need as a starting point is to type-hint data as a Union of several possible types, in your example data: Union[A, B]. But that won't be enough: if you do only that, pydantic will try to match your data to the first type it can complete, so it would still instantiate c_again.data as A(a=1), ignoring the b key/attribute. You could work around this by type-hinting B first in the union, like Union[B, A], that works but it would be neither elegant nor robust, especially if your actual use case involves many subclasses like you said.

So, you also need to add a Config to A that will make pydantic refuse to instantiate it if b is present, so it will skip it and instantiate a class of the Union that matches all the keys/attributes instead (in this case, B). Note that sublasses of A will inherit this behaviour.

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

Output:

data=B(a=1, b=0.2) ,  data=B(a=1, b=0.2)
# no assertion error
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文