Перейти к содержанию

Type hints

Type hints - (подсказки типов, тайпхинты, аннотации) - нативный способ указать тип объекта в Python.

TYPE_CHECKING

Хорошим тоном будет все тайпхинты прятать под вот такую конструкцию:

if TYPE_CHECKING:
    import my_types

TYPE_CHECKING равен False для самой программы на Python при её запуске и равен True для программ проверки типов.

Использовать такие аннотации можно следующим образом:

def fun(arg: 'my_types.Class1') -> None:
    local_var: my_types.AnotherType = fun2()

Аннотации указанные в аргументах функции должны быть заключены в кавычки, в инном случае в рантайме программы мы словим ошибку. Таким образом мы делаем "прямую ссылку" на тип и прячем его от рантайма.
Аннотации для локальных переменных не вычисляются, поэтому тут уже можно обойтись без кавычек.

Получение типа из дженерика

Задача - есть функционал, который под капотом имеет некоторый класс следующего вида:

class BaseFunction[T]:
    def serialize(self, data: dict) -> T:
        pass # тут мы используем наш serialize_to

@dataclass
class ModelA:
    x: str

class FunctionA(BaseFunction[ModelA]):
    pass

Для начала, получим __orig_bases__[0] - он вернёт нам классы, от которых мы наследовались. Так как нам нужен только наш первый класс, мы указываем [0]:

>>> FunctionA.__orig_bases__
__main__.BaseFunction[__main__.ModelA]

Ещё можно это сделать с помощью get_original_bases из types.

Теперь надо получить получить сам тип в дженерике. В этом нам поможет typing.get_args, который получает все аргументы типа. Дополнительно укажем, что нам нужен первый тип:

>>> get_args(FunctionA.__orig_bases__[0])[0]
<class '__main__.ModelA'>

Теперь в методе serialize класса BaseFunction[T] можно написать штуку, которая автоматически сериализует наши данные:

def serialize(self, data: dict) -> T:
    type_from_generic = get_args(self.__class__.__orig_bases__[0])[0]
    return type_from_generic(**data)

Проверяем:

>>> f = FunctionA()
>>> f.serialize(data={"x": 1})
ModelA(x=1)

Это же и работает с typing.Generic.

Ссылки