Приходилось ли писать сложные generic типы в Python
Что проверяют
Пользуетесь ли вы типами по делу. Знание TypeVar проверяется за минуту, интереснее, понимаете ли вы вариантность, ограничения и то, что аннотации в Python не проверяются во время выполнения.
Как отвечать
Начните с простого: обобщение появляется там, где функция или контейнер работают с любым типом, но связь между входом и выходом надо сохранить.
from typing import Protocol, TypeVar
class Comparable(Protocol):
def __lt__(self, other: "Comparable", /) -> bool: ...
T = TypeVar("T", bound=Comparable)
def top(items: list[T], n: int) -> list[T]:
return sorted(items)[-n:]Дальше по нарастанию. bound задаёт верхнюю границу, а перечисление вида TypeVar("T", int, str) означает ровно один из названных типов, это разные механизмы. Protocol даёт структурную типизацию, наследование не требуется. ParamSpec и Concatenate нужны декораторам, чтобы не терять сигнатуру обёрнутой функции. Self описывает цепочки методов, overload уточняет разное поведение по типу аргумента. В свежих версиях переменную типа можно не объявлять отдельно, параметр пишется прямо в сигнатуре.
Обязательно скажите, что проверку делает mypy или pyright в конвейере, а интерпретатор аннотации просто игнорирует.
Где спотыкаются
Думают, что аннотация что-то гарантирует во время выполнения. Без проверяющего в сборке типы остаются комментариями, и функция спокойно принимает что угодно.
Второе: ставят ковариантность, чтобы проверяющий замолчал. Список инвариантен не из вредности: в него можно писать, поэтому подстановка подтипа небезопасна. Для чтения берут Sequence или Iterable, и вопрос закрывается сам.
Третье: усложняют без нужды. Вложенные переменные типа там, где хватило бы протокола или пары перегрузок, читаются дольше, чем тело функции, и следующий человек просто снимет их вместе с пользой.