विषय पर बढ़ें

संयुक्त प्रकार

यूनियनें अन्य सभी प्रकार के पाइडेंटिक सत्यापनों से मौलिक रूप से भिन्न हैं - सभी क्षेत्रों/वस्तुओं/मानों को मान्य करने की आवश्यकता के बजाय, यूनियनों को वैध होने के लिए केवल एक सदस्य की आवश्यकता होती है।

इससे यूनियनों को मान्य करने के तरीके के बारे में कुछ बारीकियाँ सामने आती हैं:

  • आपको संघ के किस सदस्य के विरुद्ध डेटा सत्यापित करना चाहिए और किस क्रम में?
  • सत्यापन विफल होने पर कौन सी त्रुटियां उठानी चाहिए?

यूनियनों को मान्य करना सत्यापन प्रक्रिया में एक और ऑर्थोगोनल आयाम जोड़ने जैसा लगता है।

इन समस्याओं को हल करने के लिए, पाइडेंटिक यूनियनों को मान्य करने के लिए तीन मूलभूत दृष्टिकोणों का समर्थन करता है:

  1. बाएँ से दाएँ मोड - सबसे सरल तरीका, यूनियन के प्रत्येक सदस्य को क्रम में आज़माया जाता है और पहला मैच वापस कर दिया जाता है
  2. स्मार्ट मोड - "बाएँ से दाएँ मोड" के समान सदस्यों को क्रम में आज़माया जाता है; हालाँकि, बेहतर मिलान खोजने के प्रयास के लिए सत्यापन पहले मैच से आगे बढ़ेगा, यह अधिकांश यूनियन सत्यापन के लिए डिफ़ॉल्ट मोड है
  3. विभेदित संघ - विभेदक के आधार पर संघ के केवल एक सदस्य पर मुकदमा चलाया जाता है

!!! बख्शीश

In general, we recommend using [discriminated unions](#discriminated-unions). They are both more performant and more predictable than untagged unions, as they allow you to control which member of the union to validate against.

For complex cases, if you're using untagged unions, it's recommended to use `union_mode='left_to_right'` if you need guarantees about the order of validation attempts against the union members.

If you're looking for incredibly specialized behavior, you can use a [custom validator](../concepts/validators.md#field-validators).

यूनियन मोड

बाएँ से दाएँ मोड

!!! ध्यान दें क्योंकि यह मोड अक्सर अप्रत्याशित सत्यापन परिणामों की ओर ले जाता है, यह 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 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 के इनपुट के रूप में मान्य है। चूँकि इसे पहले आज़माया गया है, इसलिए हमें id str के बजाय int होने का आश्चर्यजनक परिणाम मिलता है।

स्मार्ट मोड

union_mode='left_to_right' के संभावित आश्चर्यजनक परिणामों के कारण, पाइडैंटिक >=2 में Union सत्यापन के लिए डिफ़ॉल्ट मोड union_mode='smart' है।

इस मोड में, पाइडेंटिक यूनियन सदस्यों से इनपुट के लिए सर्वोत्तम मिलान का चयन करने का प्रयास करता है। प्रदर्शन और सटीकता दोनों में सुधार की अनुमति देने के लिए पाइडेंटिक माइनर रिलीज़ के बीच सटीक एल्गोरिदम बदल सकता है।

!!! टिप्पणी

We reserve the right to tweak the internal `smart` matching algorithm in future versions of Pydantic. If you rely on very specific
matching behavior, it's recommended to use `union_mode='left_to_right'` or [discriminated unions](#discriminated-unions).

??? जानकारी "स्मार्ट मोड एल्गोरिदम"

The smart mode algorithm uses two metrics to determine the best match for the input:

1. The number of valid fields set (relevant for models, dataclasses, and typed dicts)
2. The exactness of the match (relevant for all types)

#### Number of valid fields set

!!! note
    This metric was introduced in Pydantic v2.8.0. Prior to this version, only exactness was used to determine the best match.

This metric is currently only relevant for models, dataclasses, and typed dicts.

The greater the number of valid fields set, the better the match. The number of fields set on nested models is also taken into account.
These counts bubble up to the top-level union, where the union member with the highest count is considered the best match.

For data types where this metric is relevant, we prioritize this count over exactness. For all other types, we use solely exactness.

#### Exactness

For `exactness`, Pydantic scores a match of a union member into one of the following three groups (from highest score to lowest score):

- An exact type match, for example an `int` input to a `float | int` union validation is an exact type match for the `int` member
- Validation would have succeeded in [`strict` mode](../concepts/strict_mode.md)
- Validation would have succeeded in lax mode

The union match which produced the highest exactness score will be considered the best match.

In smart mode, the following steps are taken to try to select the best match for the input:

=== "`BaseModel`, `dataclass`, and `TypedDict`"

    1. Union members are attempted left to right, with any successful matches scored into one of the three exactness categories described above,
    with the valid fields set count also tallied.
    2. After all members have been evaluated, the member with the highest "valid fields set" count is returned.
    3. If there's a tie for the highest "valid fields set" count, the exactness score is used as a tiebreaker, and the member with the highest exactness score is returned.
    4. If validation failed on all the members, return all the errors.

=== "All other data types"

    1. Union members are attempted left to right, with any successful matches scored into one of the three exactness categories described above.
        - If validation succeeds with an exact type match, that member is returned immediately and following members will not be attempted.
    2. If validation succeeded on at least one member as a "strict" match, the leftmost of those "strict" matches is returned.
    3. If validation succeeded on at least one member in "lax" mode, the leftmost match is returned.
    4. Validation failed on all the members, return all the errors.


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

!!! टिप Optional[x] प्रकार Union[x, None] का संक्षिप्त रूप है।

See more details in [Required fields](../concepts/models.md#required-fields).

भेदभावपूर्ण संघ

भेदभावपूर्ण यूनियनों को कभी-कभी "टैग की गई यूनियनें" कहा जाता है।

हम संघ के किस सदस्य के विरुद्ध सत्यापन करना है, यह चुनकर, 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]
    """

कॉल करने योग्य Discriminator के साथ भेदभाव वाली यूनियनें

??? एपीआई "एपीआई दस्तावेज़ीकरण" pydantic.types.Discriminator

कई मॉडलों वाले Union के मामले में, कभी-कभी सभी मॉडलों में एक समान फ़ील्ड नहीं होता है जिसे आप विभेदक के रूप में उपयोग कर सकते हैं। कॉल करने योग्य Discriminator के लिए यह एकदम सही उपयोग का मामला है।

from typing import Any, Literal, Union

from typing_extensions import Annotated

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'))
"""

मॉडलों और आदिम प्रकारों के संयोजन के साथ Union प्रकारों को मान्य करने के लिए Discriminator का भी उपयोग किया जा सकता है।

उदाहरण के लिए:

from typing import Any, Union

from typing_extensions import Annotated

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 त्रुटि सामने आती है।

!!! नोट typing.Annotated फ़ील्ड सिंटैक्स का उपयोग करके Union और discriminator जानकारी को फिर से समूहित करना आसान हो सकता है। अधिक विवरण के लिए अगला उदाहरण देखें.

There are a few ways to set a discriminator for a field, all varying slightly in syntax.

For `str` discriminators:
```
some_field: Union[...] = Field(discriminator='my_discriminator'
some_field: Annotated[Union[...], Field(discriminator='my_discriminator')]
```

For callable `Discriminator`s:
```
some_field: Union[...] = Field(discriminator=Discriminator(...))
some_field: Annotated[Union[...], Discriminator(...)]
some_field: Annotated[Union[...], Field(discriminator=Discriminator(...))]
```

!!! चेतावनी भेदभावपूर्ण यूनियनों का उपयोग केवल एक ही प्रकार के साथ नहीं किया जा सकता है, जैसे कि Union[Cat]

Python changes `Union[T]` into `T` at interpretation time, so it is not possible for `pydantic` to
distinguish fields of `Union[T]` from `T`.

नेस्टेड भेदभावपूर्ण यूनियनें

किसी फ़ील्ड के लिए केवल एक विभेदक सेट किया जा सकता है लेकिन कभी-कभी आप कई विभेदकों को संयोजित करना चाहते हैं। आप नेस्टेड Annotated प्रकार बनाकर ऐसा कर सकते हैं, उदाहरण के लिए:

from typing import Literal, Union

from typing_extensions import Annotated

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]
    """

!!! टिप यदि आप किसी यूनियन और केवल यूनियन के विरुद्ध डेटा को सत्यापित करना चाहते हैं, तो आप मानक BaseModel से इनहेरिट करने के बजाय पाइडेंटिक के TypeAdapter निर्माण का उपयोग कर सकते हैं।

In the context of the previous example, we have the following:

```python
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 Union

from typing_extensions import Annotated

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 सत्यापन विफल होने पर उठाए गए ValidationError का type गुण है।
  2. custom_error_message सत्यापन विफल होने पर उठाए गए ValidationError का msg गुण है।
  3. custom_error_context सत्यापन विफल होने पर उठाए गए ValidationError की ctx विशेषता है।

आप प्रत्येक मामले को Tag के साथ लेबल करके त्रुटि संदेशों को सरल भी बना सकते हैं। यह विशेष रूप से तब उपयोगी होता है जब आपके पास इस उदाहरण जैसे जटिल प्रकार हों:

from typing import Dict, List, Union

from typing_extensions import Annotated

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]
    """

本文总阅读量