跳转到内容

联合类型

联合类型与 Pydantic 验证的所有其他类型根本不同——它不需要所有字段/项/值都有效,而只需要其中一个成员有效。

这导致了关于如何验证联合类型的一些细节

  • 您应该根据联合类型的哪个或哪些成员以及以什么顺序验证数据?
  • 验证失败时应引发哪些错误?

验证联合类型感觉就像给验证过程增加了一个正交维度。

为了解决这些问题,Pydantic 支持三种验证联合类型的基本方法

  1. 从左到右模式 - 最简单的方法,按顺序尝试联合类型的每个成员,并返回第一个匹配项
  2. 智能模式 - 类似于“从左到右模式”,按顺序尝试成员;但是,验证将继续进行到第一个匹配项之后,以尝试找到更好的匹配项,这是大多数联合类型验证的默认模式
  3. 判别联合类型 - 根据判别符只尝试联合类型的一个成员

提示

通常,我们建议使用判别联合类型。它们比无标签联合类型更高效、更可预测,因为它们允许您控制根据联合类型的哪个成员进行验证。

对于复杂情况,如果您使用无标签联合类型,如果需要保证对联合成员的验证尝试顺序,建议使用 union_mode='left_to_right'

如果您正在寻找非常专业的行为,可以使用自定义验证器

联合模式

从左到右模式

注意

由于此模式通常会导致意外的验证结果,因此它不是 Pydantic >=2 中的默认设置,而是默认使用 union_mode='smart'

使用此方法,将按定义顺序对联合类型的每个成员尝试验证,并接受第一个成功的验证作为输入。

如果所有成员的验证都失败,则验证错误将包含联合类型所有成员的错误。

union_mode='left_to_right' 必须在您想要使用的联合字段上设置为 Field 参数。

具有从左到右模式的联合类型
from typing import Union

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    id: Union[str, int] = Field(union_mode='left_to_right')


print(User(id=123))
#> id=123
print(User(id='hello'))
#> id='hello'

try:
    User(id=[])
except ValidationError as e:
    print(e)
    """
    2 validation errors for User
    id.str
      Input should be a valid string [type=string_type, input_value=[], input_type=list]
    id.int
      Input should be a valid integer [type=int_type, input_value=[], input_type=list]
    """
具有从左到右模式的联合类型
from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    id: str | int = Field(union_mode='left_to_right')


print(User(id=123))
#> id=123
print(User(id='hello'))
#> id='hello'

try:
    User(id=[])
except ValidationError as e:
    print(e)
    """
    2 validation errors for User
    id.str
      Input should be a valid string [type=string_type, input_value=[], input_type=list]
    id.int
      Input should be a valid integer [type=int_type, input_value=[], input_type=list]
    """

在这种情况下,成员的顺序非常重要,如通过调整上述示例所示

从左到右的联合类型 - 意外结果
from typing import Union

from pydantic import BaseModel, Field


class User(BaseModel):
    id: Union[int, str] = Field(union_mode='left_to_right')


print(User(id=123))  # (1)
#> id=123
print(User(id='456'))  # (2)
#> id=456
  1. 正如预期的那样,输入将根据 int 成员进行验证,结果也如预期。
  2. 我们处于宽松模式,数字字符串 '123' 作为联合类型第一个成员 int 的输入是有效的。由于首先尝试它,我们得到了 idint 而不是 str 的令人惊讶的结果。
从左到右的联合类型 - 意外结果
from pydantic import BaseModel, Field


class User(BaseModel):
    id: int | str = Field(union_mode='left_to_right')


print(User(id=123))  # (1)
#> id=123
print(User(id='456'))  # (2)
#> id=456
  1. 正如预期的那样,输入将根据 int 成员进行验证,结果也如预期。
  2. 我们处于宽松模式,数字字符串 '123' 作为联合类型第一个成员 int 的输入是有效的。由于首先尝试它,我们得到了 idint 而不是 str 的令人惊讶的结果。

智能模式

由于 union_mode='left_to_right' 可能产生令人惊讶的结果,在 Pydantic >=2 中,Union 验证的默认模式是 union_mode='smart'

在此模式下,pydantic 尝试从联合成员中为输入选择最佳匹配。确切的算法可能会在 Pydantic 的次要版本之间发生变化,以允许在性能和准确性方面进行改进。

注意

我们保留在 Pydantic 未来版本中调整内部 smart 匹配算法的权利。如果您依赖于非常特定的匹配行为,建议使用 union_mode='left_to_right'判别联合类型

智能模式算法

智能模式算法使用两个指标来确定输入的最佳匹配

  1. 已设置的有效字段数量(与模型、数据类和类型化字典相关)
  2. 匹配的精确度(与所有类型相关)

已设置的有效字段数量

注意

此指标在 Pydantic v2.8.0 中引入。在此版本之前,仅使用精确度来确定最佳匹配。

此指标目前仅与模型、数据类和类型化字典相关。

已设置的有效字段数量越多,匹配越好。嵌套模型上设置的字段数量也考虑在内。这些计数会向上冒泡到顶级联合类型,其中具有最高计数的联合成员被认为是最佳匹配。

对于与此指标相关的数据类型,我们优先考虑此计数而不是精确度。对于所有其他类型,我们仅使用精确度。

精确度

对于 exactness,Pydantic 将联合成员的匹配分为以下三组(从最高分数到最低分数)

  • 精确类型匹配,例如 int 输入到 float | int 联合验证是 int 成员的精确类型匹配
  • strict 模式下验证会成功
  • 在宽松模式下验证会成功

产生最高精确度分数的联合匹配将被认为是最佳匹配。

在智能模式下,采取以下步骤尝试为输入选择最佳匹配

  1. 联合成员从左到右尝试,任何成功的匹配都会根据上述三个精确度类别之一进行评分,同时也会统计有效字段设置计数。
  2. 在评估所有成员后,返回具有最高“有效字段设置”计数的成员。
  3. 如果“有效字段设置”计数最高的情况下出现平局,则使用精确度分数作为决胜局,并返回具有最高精确度分数的成员。
  4. 如果所有成员的验证都失败,则返回所有错误。
  1. 联合成员从左到右尝试,任何成功的匹配都会根据上述三个精确度类别之一进行评分。
    • 如果验证通过精确类型匹配成功,则立即返回该成员,并且不会尝试后续成员。
  2. 如果验证至少一个成员作为“严格”匹配成功,则返回这些“严格”匹配中最左侧的一个。
  3. 如果验证至少一个成员在“宽松”模式下成功,则返回最左侧的匹配项。
  4. 所有成员的验证都失败,返回所有错误。
from typing import Union
from uuid import UUID

from pydantic import BaseModel


class User(BaseModel):
    id: Union[int, str, UUID]
    name: str


user_01 = User(id=123, name='John Doe')
print(user_01)
#> id=123 name='John Doe'
print(user_01.id)
#> 123
user_02 = User(id='1234', name='John Doe')
print(user_02)
#> id='1234' name='John Doe'
print(user_02.id)
#> 1234
user_03_uuid = UUID('cf57432e-809e-4353-adbd-9d5c0d733868')
user_03 = User(id=user_03_uuid, name='John Doe')
print(user_03)
#> id=UUID('cf57432e-809e-4353-adbd-9d5c0d733868') name='John Doe'
print(user_03.id)
#> cf57432e-809e-4353-adbd-9d5c0d733868
print(user_03_uuid.int)
#> 275603287559914445491632874575877060712
from uuid import UUID

from pydantic import BaseModel


class User(BaseModel):
    id: int | str | UUID
    name: str


user_01 = User(id=123, name='John Doe')
print(user_01)
#> id=123 name='John Doe'
print(user_01.id)
#> 123
user_02 = User(id='1234', name='John Doe')
print(user_02)
#> id='1234' name='John Doe'
print(user_02.id)
#> 1234
user_03_uuid = UUID('cf57432e-809e-4353-adbd-9d5c0d733868')
user_03 = User(id=user_03_uuid, name='John Doe')
print(user_03)
#> id=UUID('cf57432e-809e-4353-adbd-9d5c0d733868') name='John Doe'
print(user_03.id)
#> cf57432e-809e-4353-adbd-9d5c0d733868
print(user_03_uuid.int)
#> 275603287559914445491632874575877060712

判别联合类型

判别联合类型有时被称为“标记联合类型”。

我们可以使用判别联合类型更有效地验证 Union 类型,通过选择根据联合类型的哪个成员进行验证。

这使得验证更高效,并且在验证失败时也避免了错误的蔓延。

向联合类型添加判别符也意味着生成的 JSON 模式实现了相关的 OpenAPI 规范

使用 str 判别符的判别联合类型

通常,在具有多个模型的 Union 类型中,联合类型的所有成员都有一个公共字段,可用于区分应根据哪个联合类型情况验证数据;这在 OpenAPI 中称为“判别符”。

为了根据该信息验证模型,您可以在每个模型中设置相同的字段 - 让我们称之为 my_discriminator - 并带有判别值,该值是一个(或多个)Literal 值。对于您的 Union,您可以在其值中设置判别符:Field(discriminator='my_discriminator')

from typing import Literal, Union

from pydantic import BaseModel, Field, ValidationError


class Cat(BaseModel):
    pet_type: Literal['cat']
    meows: int


class Dog(BaseModel):
    pet_type: Literal['dog']
    barks: float


class Lizard(BaseModel):
    pet_type: Literal['reptile', 'lizard']
    scales: bool


class Model(BaseModel):
    pet: Union[Cat, Dog, Lizard] = Field(discriminator='pet_type')
    n: int


print(Model(pet={'pet_type': 'dog', 'barks': 3.14}, n=1))
#> pet=Dog(pet_type='dog', barks=3.14) n=1
try:
    Model(pet={'pet_type': 'dog'}, n=1)
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    pet.dog.barks
      Field required [type=missing, input_value={'pet_type': 'dog'}, input_type=dict]
    """
from typing import Literal

from pydantic import BaseModel, Field, ValidationError


class Cat(BaseModel):
    pet_type: Literal['cat']
    meows: int


class Dog(BaseModel):
    pet_type: Literal['dog']
    barks: float


class Lizard(BaseModel):
    pet_type: Literal['reptile', 'lizard']
    scales: bool


class Model(BaseModel):
    pet: Cat | Dog | Lizard = Field(discriminator='pet_type')
    n: int


print(Model(pet={'pet_type': 'dog', 'barks': 3.14}, n=1))
#> pet=Dog(pet_type='dog', barks=3.14) n=1
try:
    Model(pet={'pet_type': 'dog'}, n=1)
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    pet.dog.barks
      Field required [type=missing, input_value={'pet_type': 'dog'}, input_type=dict]
    """

使用可调用 Discriminator 的判别联合类型

API 文档

pydantic.types.Discriminator

在具有多个模型的 Union 类型中,有时没有一个统一的字段贯穿所有模型可作为判别符。这是可调用 Discriminator 的完美用例。

提示

当您设计可调用判别符时,请记住您可能需要同时考虑 dict 和模型类型输入。这种模式类似于 mode='before' 验证器,您必须预测各种形式的输入。

但是等等!您会问,我只预期传入 dict 类型,为什么我需要考虑模型?Pydantic 也使用可调用判别符进行序列化,此时您的可调用函数输入很可能是一个模型实例。

在以下示例中,您将看到可调用判别符旨在处理 dict 和模型输入。如果您不遵循此做法,很可能会在序列化期间收到警告,最坏情况下会在验证期间收到运行时错误。

from typing import Annotated, Any, Literal, Union

from pydantic import BaseModel, Discriminator, Tag


class Pie(BaseModel):
    time_to_cook: int
    num_ingredients: int


class ApplePie(Pie):
    fruit: Literal['apple'] = 'apple'


class PumpkinPie(Pie):
    filling: Literal['pumpkin'] = 'pumpkin'


def get_discriminator_value(v: Any) -> str:
    if isinstance(v, dict):
        return v.get('fruit', v.get('filling'))
    return getattr(v, 'fruit', getattr(v, 'filling', None))


class ThanksgivingDinner(BaseModel):
    dessert: Annotated[
        Union[
            Annotated[ApplePie, Tag('apple')],
            Annotated[PumpkinPie, Tag('pumpkin')],
        ],
        Discriminator(get_discriminator_value),
    ]


apple_variation = ThanksgivingDinner.model_validate(
    {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
)
print(repr(apple_variation))
"""
ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
"""

pumpkin_variation = ThanksgivingDinner.model_validate(
    {
        'dessert': {
            'filling': 'pumpkin',
            'time_to_cook': 40,
            'num_ingredients': 6,
        }
    }
)
print(repr(pumpkin_variation))
"""
ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
"""
from typing import Annotated, Any, Literal

from pydantic import BaseModel, Discriminator, Tag


class Pie(BaseModel):
    time_to_cook: int
    num_ingredients: int


class ApplePie(Pie):
    fruit: Literal['apple'] = 'apple'


class PumpkinPie(Pie):
    filling: Literal['pumpkin'] = 'pumpkin'


def get_discriminator_value(v: Any) -> str:
    if isinstance(v, dict):
        return v.get('fruit', v.get('filling'))
    return getattr(v, 'fruit', getattr(v, 'filling', None))


class ThanksgivingDinner(BaseModel):
    dessert: Annotated[
        (
            Annotated[ApplePie, Tag('apple')] |
            Annotated[PumpkinPie, Tag('pumpkin')]
        ),
        Discriminator(get_discriminator_value),
    ]


apple_variation = ThanksgivingDinner.model_validate(
    {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
)
print(repr(apple_variation))
"""
ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
"""

pumpkin_variation = ThanksgivingDinner.model_validate(
    {
        'dessert': {
            'filling': 'pumpkin',
            'time_to_cook': 40,
            'num_ingredients': 6,
        }
    }
)
print(repr(pumpkin_variation))
"""
ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
"""

Discriminator 也可以用于验证具有模型和原始类型组合的 Union 类型。

例如:

from typing import Annotated, Any, Union

from pydantic import BaseModel, Discriminator, Tag, ValidationError


def model_x_discriminator(v: Any) -> str:
    if isinstance(v, int):
        return 'int'
    if isinstance(v, (dict, BaseModel)):
        return 'model'
    else:
        # return None if the discriminator value isn't found
        return None


class SpecialValue(BaseModel):
    value: int


class DiscriminatedModel(BaseModel):
    value: Annotated[
        Union[
            Annotated[int, Tag('int')],
            Annotated['SpecialValue', Tag('model')],
        ],
        Discriminator(model_x_discriminator),
    ]


model_data = {'value': {'value': 1}}
m = DiscriminatedModel.model_validate(model_data)
print(m)
#> value=SpecialValue(value=1)

int_data = {'value': 123}
m = DiscriminatedModel.model_validate(int_data)
print(m)
#> value=123

try:
    DiscriminatedModel.model_validate({'value': 'not an int or a model'})
except ValidationError as e:
    print(e)  # (1)!
    """
    1 validation error for DiscriminatedModel
    value
      Unable to extract tag using discriminator model_x_discriminator() [type=union_tag_not_found, input_value='not an int or a model', input_type=str]
    """
  1. 请注意,如果未找到判别符值,可调用判别符函数将返回 None。当返回 None 时,将引发 union_tag_not_found 错误。
from typing import Annotated, Any

from pydantic import BaseModel, Discriminator, Tag, ValidationError


def model_x_discriminator(v: Any) -> str:
    if isinstance(v, int):
        return 'int'
    if isinstance(v, (dict, BaseModel)):
        return 'model'
    else:
        # return None if the discriminator value isn't found
        return None


class SpecialValue(BaseModel):
    value: int


class DiscriminatedModel(BaseModel):
    value: Annotated[
        (
            Annotated[int, Tag('int')] |
            Annotated['SpecialValue', Tag('model')]
        ),
        Discriminator(model_x_discriminator),
    ]


model_data = {'value': {'value': 1}}
m = DiscriminatedModel.model_validate(model_data)
print(m)
#> value=SpecialValue(value=1)

int_data = {'value': 123}
m = DiscriminatedModel.model_validate(int_data)
print(m)
#> value=123

try:
    DiscriminatedModel.model_validate({'value': 'not an int or a model'})
except ValidationError as e:
    print(e)  # (1)!
    """
    1 validation error for DiscriminatedModel
    value
      Unable to extract tag using discriminator model_x_discriminator() [type=union_tag_not_found, input_value='not an int or a model', input_type=str]
    """
  1. 请注意,如果未找到判别符值,可调用判别符函数将返回 None。当返回 None 时,将引发 union_tag_not_found 错误。

注意

使用注解模式可以方便地重组 Uniondiscriminator 信息。有关更多详细信息,请参阅下一个示例。

有几种方法可以为字段设置判别符,所有这些方法在语法上略有不同。

对于 str 判别符

some_field: Union[...] = Field(discriminator='my_discriminator')
some_field: Annotated[Union[...], Field(discriminator='my_discriminator')]

对于可调用 Discriminator

some_field: Union[...] = Field(discriminator=Discriminator(...))
some_field: Annotated[Union[...], Discriminator(...)]
some_field: Annotated[Union[...], Field(discriminator=Discriminator(...))]

警告

判别联合类型不能仅用于单个变体,例如 Union[Cat]

Python 在解释时将 Union[T] 转换为 T,因此 pydantic 无法区分 Union[T]T 的字段。

嵌套判别联合类型

一个字段只能设置一个判别符,但有时您想组合多个判别符。您可以通过创建嵌套的 Annotated 类型来实现,例如

from typing import Annotated, Literal, Union

from pydantic import BaseModel, Field, ValidationError


class BlackCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['black']
    black_name: str


class WhiteCat(BaseModel):
    pet_type: Literal['cat']
    color: Literal['white']
    white_name: str


Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]


class Dog(BaseModel):
    pet_type: Literal['dog']
    name: str


Pet = Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]


class Model(BaseModel):
    pet: Pet
    n: int


m = Model(pet={'pet_type': 'cat', 'color': 'black', 'black_name': 'felix'}, n=1)
print(m)
#> pet=BlackCat(pet_type='cat', color='black', black_name='felix') n=1
try:
    Model(pet={'pet_type': 'cat', 'color': 'red'}, n='1')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    pet.cat
      Input tag 'red' found using 'color' does not match any of the expected tags: 'black', 'white' [type=union_tag_invalid, input_value={'pet_type': 'cat', 'color': 'red'}, input_type=dict]
    """
try:
    Model(pet={'pet_type': 'cat', 'color': 'black'}, n='1')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    pet.cat.black.black_name
      Field required [type=missing, input_value={'pet_type': 'cat', 'color': 'black'}, input_type=dict]
    """

提示

如果您想验证数据(并且只验证联合类型),可以使用 pydantic 的 TypeAdapter 构造而不是继承标准 BaseModel

在前面的示例中,我们有以下内容

type_adapter = TypeAdapter(Pet)

pet = type_adapter.validate_python(
    {'pet_type': 'cat', 'color': 'black', 'black_name': 'felix'}
)
print(repr(pet))
#> BlackCat(pet_type='cat', color='black', black_name='felix')

联合类型验证错误

Union 验证失败时,错误消息可能会非常冗长,因为它会为联合类型中的每个情况生成验证错误。在处理递归模型时尤其明显,因为可能在递归的每个级别生成原因。判别联合类型在这种情况下有助于简化错误消息,因为只为具有匹配判别符值的情况生成验证错误。

您还可以通过将这些规范作为参数传递给 Discriminator 构造函数来定制 Discriminator 的错误类型、消息和上下文,如下面的示例所示。

from typing import Annotated, Union

from pydantic import BaseModel, Discriminator, Tag, ValidationError


# Errors are quite verbose with a normal Union:
class Model(BaseModel):
    x: Union[str, 'Model']


try:
    Model.model_validate({'x': {'x': {'x': 1}}})
except ValidationError as e:
    print(e)
    """
    4 validation errors for Model
    x.str
      Input should be a valid string [type=string_type, input_value={'x': {'x': 1}}, input_type=dict]
    x.Model.x.str
      Input should be a valid string [type=string_type, input_value={'x': 1}, input_type=dict]
    x.Model.x.Model.x.str
      Input should be a valid string [type=string_type, input_value=1, input_type=int]
    x.Model.x.Model.x.Model
      Input should be a valid dictionary or instance of Model [type=model_type, input_value=1, input_type=int]
    """

try:
    Model.model_validate({'x': {'x': {'x': {}}}})
except ValidationError as e:
    print(e)
    """
    4 validation errors for Model
    x.str
      Input should be a valid string [type=string_type, input_value={'x': {'x': {}}}, input_type=dict]
    x.Model.x.str
      Input should be a valid string [type=string_type, input_value={'x': {}}, input_type=dict]
    x.Model.x.Model.x.str
      Input should be a valid string [type=string_type, input_value={}, input_type=dict]
    x.Model.x.Model.x.Model.x
      Field required [type=missing, input_value={}, input_type=dict]
    """


# Errors are much simpler with a discriminated union:
def model_x_discriminator(v):
    if isinstance(v, str):
        return 'str'
    if isinstance(v, (dict, BaseModel)):
        return 'model'


class DiscriminatedModel(BaseModel):
    x: Annotated[
        Union[
            Annotated[str, Tag('str')],
            Annotated['DiscriminatedModel', Tag('model')],
        ],
        Discriminator(
            model_x_discriminator,
            custom_error_type='invalid_union_member',  # (1)!
            custom_error_message='Invalid union member',  # (2)!
            custom_error_context={'discriminator': 'str_or_model'},  # (3)!
        ),
    ]


try:
    DiscriminatedModel.model_validate({'x': {'x': {'x': 1}}})
except ValidationError as e:
    print(e)
    """
    1 validation error for DiscriminatedModel
    x.model.x.model.x
      Invalid union member [type=invalid_union_member, input_value=1, input_type=int]
    """

try:
    DiscriminatedModel.model_validate({'x': {'x': {'x': {}}}})
except ValidationError as e:
    print(e)
    """
    1 validation error for DiscriminatedModel
    x.model.x.model.x.model.x
      Field required [type=missing, input_value={}, input_type=dict]
    """

# The data is still handled properly when valid:
data = {'x': {'x': {'x': 'a'}}}
m = DiscriminatedModel.model_validate(data)
print(m.model_dump())
#> {'x': {'x': {'x': 'a'}}}
  1. custom_error_type 是验证失败时引发的 ValidationErrortype 属性。
  2. custom_error_message 是验证失败时引发的 ValidationErrormsg 属性。
  3. custom_error_context 是验证失败时引发的 ValidationErrorctx 属性。

您还可以通过使用 Tag 标记每个情况来简化错误消息。当您有像此示例中那样的复杂类型时,这特别有用

from typing import Annotated, Union

from pydantic import AfterValidator, Tag, TypeAdapter, ValidationError

DoubledList = Annotated[list[int], AfterValidator(lambda x: x * 2)]
StringsMap = dict[str, str]


# Not using any `Tag`s for each union case, the errors are not so nice to look at
adapter = TypeAdapter(Union[DoubledList, StringsMap])

try:
    adapter.validate_python(['a'])
except ValidationError as exc_info:
    print(exc_info)
    """
    2 validation errors for union[function-after[<lambda>(), list[int]],dict[str,str]]
    function-after[<lambda>(), list[int]].0
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
    dict[str,str]
      Input should be a valid dictionary [type=dict_type, input_value=['a'], input_type=list]
    """

tag_adapter = TypeAdapter(
    Union[
        Annotated[DoubledList, Tag('DoubledList')],
        Annotated[StringsMap, Tag('StringsMap')],
    ]
)

try:
    tag_adapter.validate_python(['a'])
except ValidationError as exc_info:
    print(exc_info)
    """
    2 validation errors for union[DoubledList,StringsMap]
    DoubledList.0
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
    StringsMap
      Input should be a valid dictionary [type=dict_type, input_value=['a'], input_type=list]
    """