Аннотации типов

Аннотации типов, зачем нужны и когда выполняются?

подробнее

Аннотации типов (Type Hinting) - это специальный синтаксис, позволяющий указывать ожидаемый тип данных для переменных, аргументов функций и возвращаемых значений.
Аннотации вычисляются в runtime (при работе программы), но не проверяются, результаты этих вычислений Python складывает в специальный словарь __annotations__. Python не проверяет соответствие типов.
Получается что аннотации нужны просто как подсказки для разработчика, IDE и статических анализаторов кода.

Что такое дженерики?

подробнее

более подробно тут: https://habr.com/ru/articles/947980/

В Python дженерики (generics) — это способ описать обобщённые типы, которые могут работать с разными типами данных, но при этом сохранять типовую информацию для статической проверки кода.
Представь, что ты пишешь функцию, работающую со списком чисел. Она может работать и с list[int], и с list[float]. Вместо того чтобы писать код для каждого типа отдельно, можно сказать:

“Эта функция принимает список какого-то типа T, и возвращает что-то того же типа”.

Важный момент: дженерики нужны не во время исполнения, а для статического анализа и автодополнения в IDE. В рантайме Python не проверяет типы — всё это лишь для удобства и надёжности кода.

Пример без дженериков:

def first_item(lst: list) -> object:
    return lst[0]

Минус: статическая проверка (mypy, pyright) не понимает, что если список был list[str], то результат — str.

Пример с дженериками:

from typing import TypeVar

T = TypeVar("T")  # "какой-то тип"

def first_item(lst: list[T]) -> T:
    return lst[0]

names: list[str] = ["Ann", "Bob"]
x = first_item(names)  # mypy понимает, что x — str

Пример для дженериков у классов:

from typing import TypeVar, Generic

T = TypeVar("T")

class Box(Generic[T]):
    def __init__(self, content: T):
        self.content = content

    def get(self) -> T:
        return self.content

box = Box("hello")  # mypy знает: Box[str]