Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Дженерики (Generics) в Python
Дженерики (Generics) — это механизм типизации, позволяющий писать функции, классы и структуры данных, которые работают с любыми типами данных, при этом сохраняя информацию о типе для проверки во время разработки. Дженерики обеспечивают повторное использование кода без потери типобезопасности.
Концепция дженериков
Дженерик позволяет определить класс или функцию один раз, которая будет работать с разными типами:
from typing import Generic, TypeVar
# TypeVar — переменная типа
T = TypeVar('T')
# Функция работает с любым типом
def get_first_element(items: list[T]) -> T:
return items[0]
# Использование
first_int: int = get_first_element([1, 2, 3]) # T = int
first_str: str = get_first_element(["a", "b"]) # T = str
first_float: float = get_first_element([1.5, 2.5]) # T = float
TypeVar — переменные типа
Базовое использование:
from typing import TypeVar
# Без ограничений
T = TypeVar('T')
# С ограничениями (только конкретные типы)
Numeric = TypeVar('Numeric', int, float, complex)
def process_number(value: Numeric) -> Numeric:
return value * 2
process_number(5) # OK: int
process_number(3.14) # OK: float
process_number("text") # Error: str not allowed
Ограничение с условием (Bound):
from typing import TypeVar
# T может быть любым типом, но наследует методы int
T = TypeVar('T', bound=int)
def add_one(value: T) -> T:
return value + 1 # OK: int имеет метод __add__
class MyInt(int):
pass
result = add_one(MyInt(5)) # OK: MyInt наследует int
result = add_one("text") # Error: str не наследует int
Дженерик классы
Stack (стек) как дженерик:
from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self):
self.items: list[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
if not self.items:
raise IndexError("Stack is empty")
return self.items.pop()
def is_empty(self) -> bool:
return len(self.items) == 0
# Использование
int_stack: Stack[int] = Stack()
int_stack.push(10)
int_stack.push(20)
value: int = int_stack.pop() # Type checker знает, что это int
str_stack: Stack[str] = Stack()
str_stack.push("hello")
str_stack.push("world")
word: str = str_stack.pop() # Type checker знает, что это str
Словарь (Cache) как дженерик:
from typing import Generic, TypeVar, Optional
K = TypeVar('K') # Key type
V = TypeVar('V') # Value type
class Cache(Generic[K, V]):
def __init__(self):
self.data: dict[K, V] = {}
def set(self, key: K, value: V) -> None:
self.data[key] = value
def get(self, key: K) -> Optional[V]:
return self.data.get(key)
def clear(self) -> None:
self.data.clear()
# Использование
user_cache: Cache[int, str] = Cache()
user_cache.set(1, "Alice")
user_cache.set(2, "Bob")
name: Optional[str] = user_cache.get(1) # Type: Optional[str]
Дженерик функции
from typing import TypeVar, Callable
T = TypeVar('T')
U = TypeVar('U')
# Функция трансформации
def map_function(items: list[T], func: Callable[[T], U]) -> list[U]:
return [func(item) for item in items]
# Использование
numbers = [1, 2, 3, 4, 5]
squares = map_function(numbers, lambda x: x ** 2)
print(squares) # [1, 4, 9, 16, 25]
words = ["hello", "world"]
lengths = map_function(words, len)
print(lengths) # [5, 5]
Дженерик с несколькими типами
from typing import Generic, TypeVar, Tuple
K = TypeVar('K')
V = TypeVar('V')
class Pair(Generic[K, V]):
def __init__(self, key: K, value: V):
self.key = key
self.value = value
def get_key(self) -> K:
return self.key
def get_value(self) -> V:
return self.value
def as_tuple(self) -> Tuple[K, V]:
return (self.key, self.value)
# Использование
pair: Pair[int, str] = Pair(1, "Alice")
key: int = pair.get_key()
value: str = pair.get_value()
print(pair.as_tuple()) # (1, 'Alice')
pair2: Pair[str, float] = Pair("score", 95.5)
key2: str = pair2.get_key()
value2: float = pair2.get_value()
Covariance и Contravariance
Covariant (ковариантный):
from typing import TypeVar
# Ковариантный TypeVar — может быть подтипом T
T_co = TypeVar('T_co', covariant=True)
class Producer(Generic[T_co]):
def produce(self) -> T_co:
pass
class Animal:
pass
class Dog(Animal):
pass
# Dog Producer может быть использован где ожидается Animal Producer
dog_producer: Producer[Dog] = DogProducer()
animal_producer: Producer[Animal] = dog_producer # OK
Contravariant (контравариантный):
# Контравариантный TypeVar — может быть супертипом T
T_contra = TypeVar('T_contra', contravariant=True)
class Consumer(Generic[T_contra]):
def consume(self, item: T_contra) -> None:
pass
# Animal Consumer может быть использован где ожидается Dog Consumer
animal_consumer: Consumer[Animal] = AnimalConsumer()
dog_consumer: Consumer[Dog] = animal_consumer # OK
Практические примеры
Generic Repository Pattern:
from typing import Generic, TypeVar, List, Optional
T = TypeVar('T')
class Repository(Generic[T]):
def __init__(self):
self.items: List[T] = []
def add(self, item: T) -> None:
self.items.append(item)
def find_all(self) -> List[T]:
return self.items
def find_by_index(self, index: int) -> Optional[T]:
try:
return self.items[index]
except IndexError:
return None
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
class Product:
def __init__(self, id: int, price: float):
self.id = id
self.price = price
# Использование
user_repo: Repository[User] = Repository()
user_repo.add(User(1, "Alice"))
user_repo.add(User(2, "Bob"))
product_repo: Repository[Product] = Repository()
product_repo.add(Product(1, 99.99))
users = user_repo.find_all() # List[User]
products = product_repo.find_all() # List[Product]
Дженерик Decorator:
from typing import TypeVar, Callable
from functools import wraps
T = TypeVar('T')
def timing_decorator(func: Callable[..., T]) -> Callable[..., T]:
@wraps(func)
def wrapper(*args, **kwargs) -> T:
import time
start = time.time()
result: T = func(*args, **kwargs)
print(f"Execution time: {time.time() - start:.3f}s")
return result
return wrapper
@timing_decorator
def slow_function(n: int) -> int:
return sum(range(n))
result: int = slow_function(1000000)
Преимущества дженериков
- Переиспользование кода — одна реализация для разных типов
- Типобезопасность — type checker ловит ошибки типа
- Лучшая документация — ясно, с какими типами работает код
- IDE поддержка — автодополнение и навигация работают правильно
- Рефакторинг — безопасное изменение типов
Дженерики — это мощный инструмент для написания абстрактного, переиспользуемого и типобезопасного кода на Python, особенно в больших проектах с высокими требованиями к надёжности.