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)    # True

ABC 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)  # 3

Custom 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, 1

Callable 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)  # True

Hashable 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)  # True

Virtual 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)  # True

Type 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

ABCMethods RequiredMixin Methods
Iterable__iter__-
Iterator__next____iter__
Sequence__getitem__, __len____contains__, __iter__, __reversed__, index, count
MutableSequence+ __setitem__, __delitem__, insertappend, 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, discardremove, 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: