Что такое дженерики (Generics) в Python и для чего они используются

Ответ

Дженерики (Generics) в Python — это инструмент системы статической типизации (typing), который позволяет создавать функции, классы и псевдонимы типов, способные работать с различными, заранее не определенными типами данных, сохраняя при этом строгую проверку типов.

Основная цель — написание переиспользуемого и типобезопасного кода. Статические анализаторы, такие как mypy, могут отслеживать типы и предотвращать ошибки еще до запуска программы.

Для создания дженериков используется TypeVar.

Практический пример:

Предположим, нам нужна функция, которая возвращает первый элемент из любого списка, будь то список строк или чисел. Без дженериков мы бы потеряли информацию о типе.

from typing import TypeVar, List

# 1. Создаем переменную типа (Type Variable)
# Она будет служить "заполнителем" для конкретного типа (int, str и т.д.)
T = TypeVar('T')

# 2. Используем T в аннотациях функции
# Функция принимает список элементов типа T и возвращает один элемент типа T
def get_first_element(items: List[T]) -> T:
    if not items:
        raise ValueError("Список не может быть пустым")
    return items[0]

# --- Использование ---

# mypy поймет, что first_num имеет тип int
numbers = [10, 20, 30]
first_num = get_first_element(numbers)
print(f"Первое число: {first_num}")

# mypy поймет, что first_str имеет тип str
strings = ["apple", "banana", "cherry"]
first_str = get_first_element(strings)
print(f"Первая строка: {first_str}")

# mypy выдаст ошибку, если мы попытаемся присвоить результат неверному типу
# error: Incompatible types in assignment (expression has type "int", variable has type "str")
# invalid_assignment: str = get_first_element(numbers)

Таким образом, дженерики позволяют создавать гибкие и в то же время строго типизированные компоненты, что повышает надежность и читаемость кода.

Ответ 18+ 🔞

А, дженерики в питоне! Ну, это как если бы ты, сука, пришёл на кухню и у тебя есть один универсальный нож, который похуй, что резать: хлеб, колбасу или палец, если неаккуратно. Но при этом какой-то занудный надзиратель (это mypy, блядь) стоит сбоку и орёт: "Э, ты колбасу этим ножом режешь? Так и возвращай ты мне потом нож для колбасы, а не для огурцов, ёпта!"

Короче, это фишка для статической типизации, чтобы писать код, который работает с любым типом, но при этом не превращается в тыкву в полночь, а остаётся понятным и безопасным. Без этого твоя функция — это как шлюха всея Руси: принимает всех подряд, а потом непонятно, от кого и что родится.

Вот смотри, как это выглядит без прикрас:

from typing import TypeVar, List

# 1. Вот это — наша переменная-затычка. Назовём её 'T'. Это как условный знак "сюда подставится любой тип, блядь".
T = TypeVar('T')

# 2. А теперь делаем функцию, которая эту затычку использует.
# Говорим: "Дайте мне список чего угодно (типа T), и я верну вам один элемент ЭТОГО ЖЕ ТИПА (тоже T)".
# Нельзя, сука, список чисел передать, а на выходе строку получить — `mypy` тебе мозг выест.
def get_first_element(items: List[T]) -> T:
    if not items:
        raise ValueError("Список не может быть пустым")
    return items[0]

И теперь магия, блядь:

# `mypy` видит: ага, список целых чисел. Значит, T — это int. И функция вернёт int.
numbers = [10, 20, 30]
first_num = get_first_element(numbers)  # first_num — это int, ёпта!

# А тут список строк. T — это str. Вернёт str.
strings = ["apple", "banana", "cherry"]
first_str = get_first_element(strings)  # first_str — это str, ядрёна вошь!

# А если попробуешь наебать систему и присвоить не то...
# str_variable: str = get_first_element(numbers)
# `mypy` тебе такую ошибку влепит, что мало не покажется: "Ты чё, мудак, int в str суёшь?!"

Вот и весь принцип, в рот меня чих-пых! Создаёшь абстрактную хуйню (TypeVar), используешь её в аннотациях, а потом система типов сама подставляет нужные конкретные типы, как умный пазл. Получается код, который и гибкий, и не даёт тебе выстрелить себе в ногу, потому что ты, как обычно, всё перепутал. Гениально и просто, как два пальца об асфальт!