Можно ли использовать generics в Python 3.7 и 3.8?
Что проверяют
Знаете ли вы, как менялась типизация от версии к версии и что аннотации в Python проверяет анализатор, а не интерпретатор. Следом обычно просят написать обобщённый класс и спрашивают, чем List[int] отличается от list[int].
Как отвечать
Да, дженерики есть с Python 3.5 в модуле typing. В 3.7 и 3.8 синтаксис такой:
TypeVar:T = TypeVar("T"), с ограничениямиTypeVar("N", int, float)или верхней границей черезbound=;Generic[T]: базовый класс для своего обобщённого типа,class Box(Generic[T]), дальшеBox[int]в аннотациях;- коллекции:
List[int],Dict[str, T],Optional[T],Callable[[T], bool]изtyping, потому что встроенныеlist[int]иdict[str, int]появились только в 3.9 (PEP 585).
Покажите, что видите разницу между двумя версиями. В 3.8 в typing добавили Protocol, Literal, Final и TypedDict, в 3.7 их берут из пакета typing_extensions. В 3.7 появился from __future__ import annotations: аннотации перестают вычисляться, и list[int] в сигнатуре не падает даже на старой версии. Но только в аннотациях: в псевдониме типа или базовом классе такая запись по-прежнему упадёт.
Хорошо закончить современным контекстом. Обе версии давно без поддержки, 3.8 перестала получать обновления в октябре 2024 года. В актуальном Python есть X | Y (3.10) и собственный синтаксис дженериков class Box[T]: и def first[T](items: list[T]) -> T: (3.12), без явного TypeVar.
Где спотыкаются
Говорят, что дженерики ловят ошибки «на этапе компиляции». Интерпретатор аннотации не проверяет, ошибку найдёт mypy или pyright в CI, а в рантайме код с неверными типами спокойно выполнится.
Пишут TypeVar, который встречается в сигнатуре один раз, например def size(items: List[T]) -> int. Он ничего не связывает, хватило бы Sized или Sequence[Any], а pyright на такое выдаёт предупреждение.
Пытаются проверить параметр типа в рантайме: isinstance(x, List[int]) бросает TypeError, при выполнении параметры дженериков не проверяются.