Python Abstract Classes: ABC, abstractmethod, collections.abc
An abstract base class cannot be instantiated until a subclass defines every abstract method. abc.ABC, abstract properties, collections.abc mixins and register.
- Course: Python study plan
- Module: Inheritance, protocols and duck typing
- Kind: Lesson
- Reading time: 14 min
- Runtime: CPython 3.11
What is an abstract class in Python?
An abstract class in Python is a class that inherits from abc.ABC and marks methods with @abstractmethod. It cannot be instantiated, and neither can any subclass that leaves an abstract method undefined — the TypeError names the missing methods at construction. Concrete methods on the abstract class can call the abstract ones, so shared behaviour is written once.
Lesson
Duck typing answers "can this object do X?" at the moment X is attempted. An abstract base class answers it earlier: a class that declares @abstractmethods cannot be instantiated until a subclass fills them in, so a missing method fails at construction rather than deep inside a call. The standard library's collections.abc goes further — implement the two or three core methods of Sequence or Mapping and the ABC supplies the rest. This lesson covers abc.ABC and @abstractmethod, abstract properties and class methods, collections.abc with its mixin methods, register for virtual subclasses, and isinstance against an ABC as the type check that respects duck typing.
Declaring an interface
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
@abstractmethod
def perimeter(self) -> float: ...
def describe(self) -> str: # concrete: uses the abstract ones
return f"{type(self).__name__}: area {self.area():.2f}, perimeter {self.perimeter():.2f}"
class Rect(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
Shape() # TypeError: Can't instantiate abstract class Shape with abstract methods area, perimeter
Rect(2, 3).describe() # 'Rect: area 6.00, perimeter 10.00'
ABC is the base that switches the check on; @abstractmethod marks what subclasses must provide. A subclass that leaves one out is itself abstract and cannot be instantiated — the error names the missing methods, at the point of construction. Concrete methods on the ABC (describe) are the template method pattern: shared behaviour written once in terms of the abstract operations.
The ... body is conventional for an abstract method; a docstring or raise NotImplementedError also works. An abstract method may have a body that subclasses call via super().
Abstract properties and class methods
class Plugin(ABC):
@property
@abstractmethod
def name(self) -> str: ...
@classmethod
@abstractmethod
def from_config(cls, cfg): ...
Stack @abstractmethod innermost. A subclass may satisfy an abstract property with a plain class attribute name = "csv" or a property — either makes the name resolvable, which is all the check requires.
collections.abc
The ABCs in collections.abc describe the built-in protocols and, crucially, supply mixin methods derived from a few abstract ones:
| ABC | You implement | You get for free |
|---|---|---|
Iterable | __iter__ | — |
Sized | __len__ | — |
Container | __contains__ | — |
Sequence | __getitem__, __len__ | __contains__, __iter__, __reversed__, index, count |
MutableSequence | + __setitem__, __delitem__, insert | append, extend, pop, remove, __iadd__, … |
Mapping | __getitem__, __len__, __iter__ | __contains__, keys, items, values, get, __eq__ |
MutableMapping | + __setitem__, __delitem__ | pop, popitem, clear, update, setdefault |
Set | __contains__, __iter__, __len__ | __le__, __and__, __or__, isdisjoint, … |
from collections.abc import Sequence
class Countdown(Sequence):
def __init__(self, n):
self.n = n
def __len__(self):
return self.n
def __getitem__(self, i):
if isinstance(i, slice):
return [self[j] for j in range(*i.indices(self.n))]
if i < 0:
i += self.n
if not 0 <= i < self.n:
raise IndexError(i)
return self.n - i
c = Countdown(3)
list(c), 2 in c, c.index(1), c.count(3), list(reversed(c)) # [3, 2, 1] True 2 1 [1, 2, 3]
Two methods, and the class is a full read-only sequence — with index and count you did not write, and isinstance(c, Sequence) true. MutableMapping is the way to build a dict-like class with custom storage: implement five methods and update, setdefault, pop arrive.
isinstance against an ABC
The collections.abc classes recognise duck-typed objects through __subclasshook__: isinstance(x, Iterable) is true for anything with __iter__, whether or not it inherits from Iterable. That makes ABCs the right target for the boundary checks of the previous lesson:
from collections.abc import Iterable, Mapping
def flatten(items):
for x in items:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
yield from flatten(x)
else:
yield x
def merge(a: Mapping, b: Mapping) -> dict: ...
Iterable rather than list accepts tuples, sets, generators and custom classes; the str exclusion is the string-versus-sequence rule again. Hashable, Callable, Sized work the same way. For your own ABCs the hook is not automatic — a class must inherit, or be registered.
register: virtual subclasses
class Drawable(ABC):
@abstractmethod
def draw(self): ...
Drawable.register(SomeThirdPartyClass) # promises it has draw(); nothing is checked
isinstance(SomeThirdPartyClass(), Drawable) # True
Registration makes isinstance say yes for a class you cannot edit, without inheritance and without verification. It is rare, and the Protocol of lesson 5 is the modern answer to the same need.
ABC versus duck typing versus Protocol
An ABC is right when there is shared implementation to inherit (template methods, mixins from collections.abc) or when a construction-time guarantee matters (a plugin system that must fail fast on an incomplete plugin). Duck typing is right when the code simply calls methods and any provider will do. A Protocol (lesson 5) is right when you want the static checker to verify the duck typing without requiring inheritance. All three coexist: your Shape ABC's subclasses are also ducks to any code that calls .area().
Pitfalls
- Forgetting
ABCas a base (ormetaclass=ABCMeta), so@abstractmethodis not enforced. - Stacking
@abstractmethodoutermost over@property— it must be innermost. - Subclassing
Sequencebut not handlingslicein__getitem__. isinstance(x, list)at a boundary whereSequenceorIterablewas meant.isinstance(s, Iterable)letting a string through as a sequence of characters.- Expecting
registerto check anything.
Key takeaways
class X(ABC)with@abstractmethods cannot be instantiated until every abstract method is defined; the error names them at construction.- Concrete methods on an ABC are template methods written against the abstract ones.
collections.abc.Sequence/Mapping/Setsupply the full protocol from two or three core methods.isinstance(x, Iterable)(and friends) recognise duck-typed objects; check against ABCs, not concrete types, and excludestrfrom sequence checks.- Use an ABC for shared implementation or fail-fast guarantees; duck typing or a
Protocolotherwise.
Common questions
How do I fix TypeError: Can't instantiate abstract class?
The class, or a base it inherits from, still has an @abstractmethod with no implementation, and the error lists which ones. Define every listed method in the subclass you are instantiating; a subclass that leaves one out is itself abstract.
What is collections.abc used for?
collections.abc holds abstract base classes for the built-in protocols — Iterable, Sequence, Mapping, Set and more. Subclass one and implement its core methods, and it supplies the rest: a Sequence needs only __getitem__ and __len__ to gain __contains__, __iter__, __reversed__, index and count.
How do I make an abstract property in Python?
Stack the decorators with @abstractmethod innermost: @property on top, then @abstractmethod, then the def. A subclass satisfies it with a property or simply with a class attribute of the same name, such as name = "csv".
Why does isinstance(x, Iterable) work without inheriting from Iterable?
The collections.abc classes define __subclasshook__, which recognises any class with the right methods, so anything with __iter__ counts as Iterable. Your own ABCs do not do this automatically: a class must inherit from them or be registered.
When should I use an abstract base class instead of duck typing?
Use an ABC when subclasses share real implementation, such as template methods or collections.abc mixins, or when an incomplete class must fail at construction, as in a plugin system. When code just calls methods and any provider will do, duck typing or a Protocol is enough.
Exercises
Shapes on an ABC
Define Shape(ABC) with abstract area() and perimeter() and a concrete template method describe() returning <ClassName>: area <a>, perimeter <p> with two decimals. Implement Rect(w, h) and Circle(r). Read shape lines and print their descriptions; a line shape attempts Shape() and prints abstract when TypeError is raised.
Input: lines rect w h, circle r or shape. Output: one line per input line.
rect 2 3
shape
circle 1
prints
Rect: area 6.00, perimeter 10.00
abstract
Circle: area 3.14, perimeter 6.28A sequence from two methods
Implement Evens(n) — the first n even numbers 0, 2, 4, … — as a subclass of collections.abc.Sequence, defining only __len__ and __getitem__ (with negative indexes, IndexError past the ends, and slices). Then use the inherited mixin methods: read n and a query value q; print the elements, q in, index(q) (or absent on ValueError), count(q), the reversed elements, and isinstance(evens, Sequence).
Input: n q. Output: elements <…>, in <bool>, index <i> or index absent, count <c>, reversed <…>, sequence <bool>.
4 4
prints
elements 0 2 4 6
in True
index 2
count 1
reversed 6 4 2 0
sequence TrueIn this module: Inheritance, protocols and duck typing
- Inheritance basics — subclasses, super() and attribute lookup
- Polymorphism and duck typing — EAFP, hasattr and programming to behaviour
- Abstract base classes — abc and collections.abc (this lesson)
- Multiple inheritance and the MRO — mixins and cooperative super()
- Protocols and structural typing — typing.Protocol
- Composition over inheritance — Liskov, delegation and wrapping built-ins
- Checkpoint — Inheritance, protocols and duck typing
← Polymorphism and duck typing — EAFP, hasattr and programming to behaviour · Multiple inheritance and the MRO — mixins and cooperative super() →