Прямая реализация `typing.Union` в Python - PullRequest
1 голос
/ 07 января 2020

Я хотел бы создать экземпляр , набрав Union двух классов, полученных из pydantic.BaseModel напрямую. Однако я получаю TypeError: Cannot instantiate typing.Union.

Все примеры, которые я видел, объявляют Union атрибутом класса (например, здесь ).

Ниже приведен минимальный пример, который я хотел бы использовать.

from pydantic import BaseModel
from typing import Union

class A(BaseModel):
    a: int

class B(A):
    b: int

class C(A):
    c: str

MyUnion = Union[B, C, A]
mu = MyUnion(a=666, c='foo')  #  This command throws the TypeError

Есть ли способ добиться этого?

Вот ошибка, которую я получаю

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-40-8163e3490185> in <module>
----> 1 MyUnion()

c:\program files\python37\lib\typing.py in __call__(self, *args, **kwargs)
    668             raise TypeError(f"Type {self._name} cannot be instantiated; "
    669                             f"use {self._name.lower()}() instead")
--> 670         result = self.__origin__(*args, **kwargs)
    671         try:
    672             result.__orig_class__ = self

c:\program files\python37\lib\typing.py in __call__(self, *args, **kwds)
    327
    328     def __call__(self, *args, **kwds):
--> 329         raise TypeError(f"Cannot instantiate {self!r}")
    330
    331     def __instancecheck__(self, obj):

TypeError: Cannot instantiate typing.Union

Ответы [ 2 ]

3 голосов
/ 07 января 2020

Союз работает не так.

Объединение - это то же самое, что и union в C.

Это означает, что переменная может быть типа A или типа B.

Например,

def f(a: Union[int, str]) -> None:
   ...

Это означает, что a может быть int или str, подклассом тех и ничего больше.

1 голос
/ 07 января 2020

Похоже, что вам нужна фабричная функция, которая угадывает, какую из A, B или C создать, основываясь на аргументах ключевых слов, представленных в вызове.

Для пример:

from pydantic import BaseModel
from typing import Union

class A(BaseModel):
    a: int

class B(A):
    b: int

class C(A):
    c: str

def a_b_or_c(**kwargs) -> Union[B, C, A]:
    if 'c' in kwargs:
        return C(**kwargs)
    elif 'b' in kwargs:
        return B(**kwargs)
    elif 'a' in kwargs:
        return A(**kwargs)
    else:
        raise Exception("I don't know what class you want")

my = a_b_or_c(a=666, c='foo')

a_b_or_c, конечно, может сделать более обширное тестирование аргументов, найденных в kwargs, например, чтобы предотвратить передачу аргументов, которые ни один из A, B или C ожидают.

...