Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
collections.abc: Абстрактные базовые классы для контейнеров
collections.abc — это модуль Python, содержащий абстрактные базовые классы (Abstract Base Classes, ABC) для определения интерфейсов стандартных типов данных, таких как списки, словари, множества и итераторы. Это фундамент для написания правильного полиморфного кода в Python.
Назначение
Модуль collections.abc решает проблему: как определить, что объект ведет себя как список, словарь или итератор, не зависея от конкретного класса?
# Без ABC: плохо
def print_items(container):
if isinstance(container, list):
# обработка списка
elif isinstance(container, dict):
# обработка словаря
elif isinstance(container, tuple):
# обработка кортежа
# ... еще 10 типов?
# С ABC: хорошо
from collections.abc import Iterable
def print_items(container):
if isinstance(container, Iterable):
for item in container:
print(item)
# Работает с ЛЮБЫМ итерируемым объектом!
Основные ABC классы
1. Iterable — объект, который можно перебирать
from collections.abc import Iterable
# Встроенные типы
assert isinstance([1, 2, 3], Iterable) # list
assert isinstance({1, 2, 3}, Iterable) # set
assert isinstance("abc", Iterable) # str
assert isinstance(range(5), Iterable) # range
# Создаем свой итерируемый класс
class CountUp(Iterable):
def __init__(self, max):
self.max = max
def __iter__(self): # Обязателен для Iterable
return iter(range(self.max))
counter = CountUp(5)
for num in counter: # Работает!
print(num) # 0, 1, 2, 3, 4
2. Iterator — объект с методами iter и next
from collections.abc import Iterator
class FibonacciIterator(Iterator):
def __init__(self, max):
self.a, self.b = 0, 1
self.max = max
def __iter__(self):
return self
def __next__(self):
if self.a > self.max:
raise StopIteration
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
fib = FibonacciIterator(20)
print(list(fib)) # [0, 1, 1, 2, 3, 5, 8, 13]
3. Container — объект, поддерживающий оператор in
from collections.abc import Container
class BlackList(Container):
def __init__(self, banned_words):
self.banned = set(banned_words)
def __contains__(self, word): # Обязателен для Container
return word.lower() in self.banned
blacklist = BlackList(["spam", "hate"])
assert "spam" in blacklist # True
assert "hello" not in blacklist # True
4. Sequence — упорядоченный контейнер с индексацией
from collections.abc import Sequence
class RangeWrapper(Sequence):
def __init__(self, start, stop):
self.range = range(start, stop)
def __getitem__(self, index): # Обязателен
return self.range[index]
def __len__(self): # Обязателен
return len(self.range)
seq = RangeWrapper(0, 10)
assert seq[5] == 5 # Индексация работает
assert len(seq) == 10 # len() работает
assert 7 in seq # Проверка принадлежности работает
5. Mapping — словаро-подобный контейнер
from collections.abc import Mapping
class CaseInsensitiveDict(Mapping):
def __init__(self, data):
self._store = {k.lower(): v for k, v in data.items()}
def __getitem__(self, key):
return self._store[key.lower()]
def __iter__(self):
return iter(self._store)
def __len__(self):
return len(self._store)
config = CaseInsensitiveDict({"Debug": True, "HOST": "localhost"})
assert config["debug"] == True # Работает!
assert config["host"] == "localhost" # Регистронезависимый доступ
6. MutableSequence и MutableMapping — изменяемые версии
from collections.abc import MutableSequence
class Stack(MutableSequence):
def __init__(self):
self._items = []
def __getitem__(self, index):
return self._items[index]
def __setitem__(self, index, value):
self._items[index] = value
def __delitem__(self, index):
del self._items[index]
def __len__(self):
return len(self._items)
def insert(self, index, value): # Обязателен для MutableSequence
self._items.insert(index, value)
def push(self, value):
self.append(value)
def pop(self):
return self._items.pop()
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
assert stack.pop() == 3
assert len(stack) == 2
Полная иерархия ABC
Iterable — может быть пройден в цикле for
├── Iterator — имеет __iter__ и __next__
├── Reversible — поддерживает reversed()
├── Generator — функция с yield
└── ...
Container — поддерживает оператор in
Sized — поддерживает len()
Callable — может быть вызван (функции, классы)
Collection — Iterable + Container + Sized
├── Sequence — упорядоченный контейнер
│ └── MutableSequence — изменяемый
├── Set — неупорядоченный уникальный
│ └── MutableSet — изменяемое множество
└── Mapping — ключ-значение
└── MutableMapping — изменяемый словарь
Практические примеры
Пример 1: Функция для работы с любыми контейнерами
from collections.abc import Sequence, Mapping, Iterable
def describe_container(obj):
"""Выводит информацию о типе контейнера"""
if isinstance(obj, Mapping):
print(f"Mapping: {len(obj)} пар ключ-значение")
for k, v in obj.items():
print(f" {k}: {v}")
elif isinstance(obj, Sequence) and not isinstance(obj, str):
print(f"Sequence: {len(obj)} элементов")
print(f" {obj}")
elif isinstance(obj, Iterable):
print(f"Iterable: {list(obj)}")
else:
print(f"Unknown: {obj}")
describe_container({"a": 1, "b": 2}) # Mapping
describe_container([1, 2, 3]) # Sequence
describe_container((1, 2, 3)) # Sequence (кортеж)
describe_container(range(5)) # Sequence
describe_container("hello") # Не выведет Sequence (str исключение)
describe_container({1, 2, 3}) # Iterable
Пример 2: Type hints с ABC
from collections.abc import Mapping, MutableSequence
from typing import TypeVar
T = TypeVar('T')
def process_list(items: MutableSequence[T]) -> None:
"""Принимает любой изменяемый список (list, deque и т.д.)"""
items.append("processed")
items[0] = "first"
def extract_values(config: Mapping[str, T]) -> list[T]:
"""Принимает любой словаро-подобный объект"""
return list(config.values())
# Работает с разными типами:
my_list = [1, 2, 3]
process_list(my_list) # OK
from collections import deque
my_deque = deque([1, 2, 3])
process_list(my_deque) # OK
my_dict = {"a": 1, "b": 2}
print(extract_values(my_dict)) # [1, 2]
Пример 3: Проверка соответствия интерфейсу
from collections.abc import Mapping
def save_config(config: Mapping[str, str], filename: str):
"""Проверяет, что это действительно Mapping"""
# isinstance уже проверяет, но можно явно
if not isinstance(config, Mapping):
raise TypeError(f"Expected Mapping, got {type(config)}")
with open(filename, 'w') as f:
for key, value in config.items():
f.write(f"{key}={value}\n")
# Работает с dict
save_config({"debug": "true"}, "app.conf")
# Работает с CaseInsensitiveDict
save_config(CaseInsensitiveDict({"Debug": "true"}), "app.conf")
# Не работает с list
try:
save_config(["a", "b"], "app.conf") # TypeError
except TypeError as e:
print(e) # Expected Mapping, got <class 'list'>
Зачем это нужно
1. Полиморфизм утиного типирования
# Не нужно знать, какой конкретно класс
# Нужно знать, какой интерфейс он поддерживает
def sum_items(items: Iterable[int]) -> int:
return sum(items)
sum_items([1, 2, 3]) # list
sum_items((1, 2, 3)) # tuple
sum_items({1, 2, 3}) # set
sum_items(range(1, 4)) # range
# Все работает!
2. Правильные type hints
# Плохо
def process(data: list) -> list:
# Работает только со списками
pass
# Хорошо
from collections.abc import Sequence
def process(data: Sequence) -> Sequence:
# Работает с любым упорядоченным контейнером
pass
3. Duck typing с проверкой
if isinstance(obj, Callable):
# Объект можно вызвать как функцию
result = obj()
if isinstance(obj, Iterable):
# Объект можно перебирать
for item in obj:
pass
Резюме
collections.abc предоставляет абстрактные базовые классы для стандартных типов данных (итераторы, контейнеры, маппинги и т.д.). Использование ABC позволяет писать гибкий полиморфный код, использовать правильные type hints и проверять, что объект поддерживает нужный интерфейс. Это ключ к правильной архитектуре в Python.