The collections.abc module provides abstract base classes for container types. Inherit from them to create custom collections that work with Python's built-in functions and idioms.
Type Checking with ABCs
from collections.abc import Iterable, Sequence, Mapping, Set
# Check if object is iterable
isinstance([1, 2, 3], Iterable) # True
isinstance("hello", Iterable) # True
isinstance(42, Iterable) # False
# Check specific container types
isinstance([1, 2], Sequence) # True
isinstance({1, 2}, Set) # True
isinstance({'a': 1}, Mapping) # TrueABC Hierarchy
from collections.abc import (
Container, # __contains__
Iterable, # __iter__
Iterator, # __iter__, __next__
Reversible, # __reversed__
Collection, # Container + Iterable + Sized
Sequence, # Collection + __getitem__, __len__
MutableSequence,# Sequence + __setitem__, __delitem__, insert
Set, # Collection + comparisons
MutableSet, # Set + add, discard
Mapping, # Collection + __getitem__, keys, values, items
MutableMapping, # Mapping + __setitem__, __delitem__
)Custom Sequence
from collections.abc import Sequence
class MyRange(Sequence):
"""Custom range-like sequence."""
def __init__(self, start: int, stop: int):
self.start = start
self.stop = stop
def __getitem__(self, index):
if isinstance(index, slice):
return [self[i] for i in range(*index.indices(len(self)))]
if index < 0:
index += len(self)
if not 0 <= index < len(self):
raise IndexError("Index out of range")
return self.start + index
def __len__(self):
return max(0, self.stop - self.start)
r = MyRange(5, 10)
print(list(r)) # [5, 6, 7, 8, 9]
print(7 in r) # True (inherited from Sequence)
print(r.index(7)) # 2 (inherited)
print(r.count(7)) # 1 (inherited)Custom Mutable Sequence
from collections.abc import MutableSequence
class TrackedList(MutableSequence):
"""List that tracks modifications."""
def __init__(self, data=None):
self._data = list(data) if data else []
self._modifications = 0
def __getitem__(self, index):
return self._data[index]
def __setitem__(self, index, value):
self._data[index] = value
self._modifications += 1
def __delitem__(self, index):
del self._data[index]
self._modifications += 1
def __len__(self):
return len(self._data)
def insert(self, index, value):
self._data.insert(index, value)
self._modifications += 1
lst = TrackedList([1, 2, 3])
lst.append(4) # Inherited method
lst.extend([5, 6]) # Inherited method
print(lst._modifications) # 3Custom Mapping
from collections.abc import MutableMapping
class CaseInsensitiveDict(MutableMapping):
"""Dictionary with case-insensitive keys."""
def __init__(self, data=None):
self._data = {}
if data:
self.update(data)
def _normalize(self, key):
return key.lower() if isinstance(key, str) else key
def __getitem__(self, key):
return self._data[self._normalize(key)]
def __setitem__(self, key, value):
self._data[self._normalize(key)] = value
def __delitem__(self, key):
del self._data[self._normalize(key)]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
d = CaseInsensitiveDict({'Name': 'Alice'})
print(d['name']) # Alice
print(d['NAME']) # Alice
d['AGE'] = 30
print(list(d.keys())) # ['name', 'age']Custom Set
from collections.abc import MutableSet
class UniqueQueue(MutableSet):
"""Queue that only keeps unique items."""
def __init__(self, items=None):
self._set = set()
self._list = []
if items:
for item in items:
self.add(item)
def __contains__(self, item):
return item in self._set
def __iter__(self):
return iter(self._list)
def __len__(self):
return len(self._set)
def add(self, item):
if item not in self._set:
self._set.add(item)
self._list.append(item)
def discard(self, item):
if item in self._set:
self._set.discard(item)
self._list.remove(item)
q = UniqueQueue([1, 2, 2, 3, 1])
print(list(q)) # [1, 2, 3]Iterator Protocol
from collections.abc import Iterator
class Countdown(Iterator):
"""Countdown iterator."""
def __init__(self, start: int):
self.current = start
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for num in Countdown(5):
print(num) # 5, 4, 3, 2, 1Callable ABC
from collections.abc import Callable
def accepts_callback(callback: Callable[[int], str]) -> None:
result = callback(42)
print(result)
# Functions are callable
def my_func(x: int) -> str:
return str(x)
isinstance(my_func, Callable) # True
# Classes with __call__ are callable
class MyCallable:
def __call__(self, x: int) -> str:
return str(x * 2)
isinstance(MyCallable(), Callable) # TrueHashable ABC
from collections.abc import Hashable
# Check if usable as dict key
isinstance(42, Hashable) # True
isinstance("hello", Hashable) # True
isinstance((1, 2), Hashable) # True
isinstance([1, 2], Hashable) # False
isinstance({'a': 1}, Hashable) # False
# Custom hashable class
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
isinstance(Point(1, 2), Hashable) # TrueVirtual Subclasses
from collections.abc import Sequence
# Register existing class as virtual subclass
class ExternalList:
def __getitem__(self, index):
return index * 2
def __len__(self):
return 10
Sequence.register(ExternalList)
isinstance(ExternalList(), Sequence) # TrueType Hints with ABCs
from collections.abc import Mapping, Sequence, Iterable
from typing import TypeVar
K = TypeVar('K')
V = TypeVar('V')
T = TypeVar('T')
def process_items(items: Iterable[T]) -> list[T]:
return [process(item) for item in items]
def merge_dicts(d1: Mapping[K, V], d2: Mapping[K, V]) -> dict[K, V]:
return {**d1, **d2}
def first_n(seq: Sequence[T], n: int) -> list[T]:
return list(seq[:n])Sized, Container, Iterable
from collections.abc import Sized, Container, Iterable, Collection
class DataStore:
def __init__(self, items):
self._items = list(items)
# Sized
def __len__(self):
return len(self._items)
# Container
def __contains__(self, item):
return item in self._items
# Iterable
def __iter__(self):
return iter(self._items)
store = DataStore([1, 2, 3])
isinstance(store, Sized) # True
isinstance(store, Container) # True
isinstance(store, Iterable) # True
isinstance(store, Collection) # True (all three)ABCs Reference
| ABC | Methods Required | Mixin Methods |
|---|---|---|
Iterable | __iter__ | - |
Iterator | __next__ | __iter__ |
Sequence | __getitem__, __len__ | __contains__, __iter__, __reversed__, index, count |
MutableSequence | + __setitem__, __delitem__, insert | append, clear, reverse, extend, pop, remove, __iadd__ |
Mapping | __getitem__, __iter__, __len__ | __contains__, keys, items, values, get, __eq__, __ne__ |
MutableMapping | + __setitem__, __delitem__ | pop, popitem, clear, update, setdefault |
Set | __contains__, __iter__, __len__ | comparisons, __and__, __or__, __sub__, __xor__, isdisjoint |
MutableSet | + add, discard | remove, pop, clear, __ior__, __iand__, __ixor__, __isub__ |
Use collections.abc to build collections that feel native to Python—supporting len(), in, iteration, and all the operations users expect.
React to this post: