Вопрос проверяет понимание дженериков (Generics) в Python для создания гибких и типобезопасных функций и классов, работающих с разными типами данных.
Дженерики (Generics) в Python — это механизм, который позволяет определять функции, классы или структуры данных, способные работать с различными типами, при этом сохраняя информацию о конкретном типе для статических проверок. Они реализуются через модуль typing, который вводит аннотации типов, поддерживающие параметрический полиморфизм.
Ключевые элементы для создания дженериков:
TypeVar: Определяет переменную типа, которая может быть заменена конкретным типом при использовании.List[T], Dict[K, V] из модуля typing или пользовательские классы, наследующие от Generic[T].Рассмотрим простой пример функции, которая возвращает первый элемент списка любого типа:
from typing import TypeVar, List
T = TypeVar('T') # Определяем переменную типа
def first_element(items: List[T]) -> T:
"""Возвращает первый элемент списка."""
return items[0]
# Использование
numbers: List[int] = [1, 2, 3]
result: int = first_element(numbers) # mypy понимает, что result имеет тип int
print(result) # 1
Здесь T — это переменная типа, которая связывает тип элементов списка с типом возвращаемого значения. При вызове с List[int] функция возвращает int.
Можно создавать собственные классы, которые параметризуются типами. Например, простой контейнер:
from typing import Generic, TypeVar
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, content: T) -> None:
self.content = content
def get_content(self) -> T:
return self.content
# Использование
box_int: Box[int] = Box(42)
value: int = box_int.get_content() # Тип value выводится как int
box_str: Box[str] = Box("hello")
text: str = box_str.get_content() # Тип text выводится как str
Класс Box может хранить объект любого типа, но при создании экземпляра мы указываем конкретный тип, что позволяет статическому анализатору проверять корректность операций.
Дженерики особенно полезны в следующих сценариях:
С помощью TypeVar можно задавать ограничения (bounds) или требовать, чтобы тип был определённого класса. Например:
from typing import TypeVar, List
# T должен быть подтипом float или int (числовой тип)
Numeric = TypeVar('Numeric', int, float)
def sum_values(values: List[Numeric]) -> Numeric:
return sum(values)
# Корректно
sum_values([1, 2, 3]) # int
sum_values([1.5, 2.5]) # float
# Ошибка типа (если проверить mypy): str не входит в int/float
# sum_values(["a", "b"])
Также можно использовать typing.Generic с несколькими параметрами, например, для словаря: Dict[K, V].
Вывод: Дженерики в Python через модуль typing стоит применять, когда нужно создать гибкий, повторно используемый код, который должен оставаться типобезопасным. Они особенно полезны в библиотеках, фреймворках и любом проекте, где используется статическая проверка типов (mypy, pyright), так как помогают избежать ошибок типа и улучшают читаемость кода.