← Назад к вопросам

Что такое collections.abc?

1.7 Middle🔥 191 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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.