An abstract base class (ABC), provided by the abc module, is a class that cannot be instantiated directly and may declare abstract methods that concrete subclasses are required to override. Defined in PEP 3119, ABCs formalise interfaces and enable isinstance() checks against capabilities.
Contracts for subclasses
What you will learn: what an abstract base class is, how @abstractmethod forces subclasses to implement methods, and why the error helpfully fires at instantiation time.
How to read this tab: Try creating an incomplete subclass and watch the TypeError appear the moment you instantiate it.
An abstract base class is a contract. It says "any class that claims to be one of these must provide these specific methods." Python refuses to create an object from a class that hasn't fulfilled the contract — so mistakes are caught immediately, not deep in your program.
The error happens at instantiation — exactly when you want it. Defining an incomplete subclass is allowed; the TypeError fires the moment you try to create an instance. You can never accidentally pass around an object missing required methods.
The problem ABCs solve
Without an ABC, nothing stops you from creating an incomplete subclass and only discovering the missing method when it is called — possibly much later, in production. An ABC moves that error to the moment of object creation.
from abc import ABC, abstractmethod
class Shape(ABC): # inherit from ABC
@abstractmethod
def area(self):
... # no implementation — subclasses must provide it
@abstractmethod
def perimeter(self):
...
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
c = Circle(5) # works — both methods implemented
print(c.area()) # 78.53...
# You cannot instantiate the ABC itself
# Shape() # TypeError: Can't instantiate abstract class Shape
# A subclass that forgets a method also can't be instantiated
class Square(Shape):
def area(self):
return 4
# forgot perimeter()
# Square() # TypeError: Can't instantiate abstract class Square
# # with abstract method perimeterDefining an incomplete subclass is allowed; the TypeError fires the moment you try to create an instance. This is exactly when you want it — you cannot accidentally pass around an object that is missing required methods.
✅ Beginner tab complete
- I can define an ABC with @abstractmethod
- I know an ABC cannot be instantiated directly
- I know a subclass missing an abstract method cannot be instantiated
- I know the error fires at instantiation, not definition
Abstract properties, partial implementations, virtual subclasses
What you will learn: abstract properties, ABCs that mix concrete and abstract methods, and register() for virtual subclasses.
How to read this tab: Note that register() makes isinstance pass but does NOT enforce the methods — it is a promise.
ABCs can provide working methods too. An ABC is not all-or-nothing — it can mix abstract methods (subclasses must implement) with concrete methods (inherited as-is). This lets you build a base that does the shared work and only requires subclasses to fill specific gaps.
Abstract properties and partial implementations
ABCs can declare abstract properties and can also provide concrete (working) methods alongside abstract ones — giving subclasses a base to build on while still enforcing the required pieces.
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name):
self.name = name
@property
@abstractmethod
def monthly_salary(self):
... # abstract property — must be overridden
@abstractmethod
def role(self):
...
def annual_salary(self): # CONCRETE method — inherited as-is
return self.monthly_salary * 12
def describe(self): # concrete, uses abstract methods
return f"{self.name} ({self.role()}): {self.annual_salary()}/yr"
class Manager(Employee):
@property
def monthly_salary(self):
return 150_000
def role(self):
return "Manager"
m = Manager("Priya")
print(m.annual_salary()) # 1800000 — concrete method works
print(m.describe()) # Priya (Manager): 1800000/yrregister() is a promise, not a check. It makes isinstance/issubclass return True without the class inheriting — useful for retrofitting interfaces onto third-party classes. But unlike real inheritance, it does NOT verify the required methods exist. The guarantee is yours to keep.
Virtual subclasses with register()
An ABC can recognise a class as a subclass without that class inheriting from it — via register(). This is how Python lets unrelated classes satisfy an interface, the basis of "duck typing made checkable."
from abc import ABC
class Serialisable(ABC):
@abstractmethod
def to_json(self): ...
# An existing class we don't want to (or can't) modify
class LegacyRecord:
def to_json(self):
return "{}"
# Register it as a virtual subclass
Serialisable.register(LegacyRecord)
print(issubclass(LegacyRecord, Serialisable)) # True
print(isinstance(LegacyRecord(), Serialisable)) # True
# NOTE: register() does NOT enforce the methods exist — it's a promise.
# Use it for retrofitting interfaces onto existing/third-party classes.
# __subclasshook__ — recognise ANY class with the right method
class Sized(ABC):
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
if any("__len__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
print(issubclass(list, Sized)) # True — list has __len__✅ Intermediate tab complete
- I can declare an abstract property
- I can mix concrete methods with abstract ones in an ABC
- I can register a virtual subclass with ABC.register()
- I know register() does not verify the methods exist
ABCMeta, collections.abc, and free mixin methods
What you will learn: how ABCMeta overrides isinstance/issubclass, the collections.abc container ABCs, and the mixin methods you get for free.
How to read this tab: Inherit from collections.abc.Sequence and implement just two methods to get six more automatically.
collections.abc and the ABCMeta machinery
ABCs are implemented through the ABCMeta metaclass. When a class uses ABCMeta (which ABC does via inheritance), the metaclass tracks abstract methods in __abstractmethods__ and overrides __instancecheck__ and __subclasscheck__ so that isinstance and issubclass consult registered virtual subclasses and __subclasshook__. The standard library ships a complete set of ABCs in collections.abc describing Python's container protocols.
from collections.abc import (
Iterable, Iterator, Sequence, MutableSequence,
Mapping, MutableMapping, Set, Hashable, Callable
)
# These ABCs let you check capabilities, not concrete types
print(isinstance([], Sequence)) # True
print(isinstance({}, Mapping)) # True
print(isinstance("abc", Iterable)) # True
print(isinstance(len, Callable)) # True
print(isinstance(42, Hashable)) # True
# Inheriting from a collections.abc ABC gives you mixin methods free
class MyList(Sequence):
def __init__(self, data):
self._data = data
def __getitem__(self, i):
return self._data[i]
def __len__(self):
return len(self._data)
# Sequence provides __contains__, __iter__, __reversed__,
# index(), and count() automatically from these two methods
ml = MyList([10, 20, 30])
print(20 in ml) # True — __contains__ provided by Sequence
print(list(reversed(ml)))# [30,20,10] — __reversed__ provided
print(ml.index(20)) # 1 — index() provided
# __abstractmethods__ holds the set still needing implementation
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def foo(self): ...
print(Base.__abstractmethods__) # frozenset({'foo'})The mixin behaviour is the practical payoff: inherit from collections.abc.Sequence and implement just __getitem__ and __len__, and you get __contains__, __iter__, __reversed__, index, and count for free. This is why building custom containers on the collections.abc ABCs is far less work than implementing every protocol method by hand.
✅ Expert tab complete
- I know collections.abc defines Iterable, Sequence, Mapping, etc.
- I know inheriting from Sequence gives __contains__/__iter__/index/count free
- I know __subclasshook__ can recognise any class with the right methods