Abstract Base Classes (abc)
Reviewed & published by Brayan K
An abstract base class is a class you can't instantiate directly — it declares methods that every subclass must implement, and Python enforces that contract by raising a TypeError if a subclass leaves any of them out.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
ABCs let you design a clear interface up front — "every payment method needs a pay() and a refund()" — and catch incomplete classes the moment someone tries to use them, instead of much later when the missing method blows up.
What You'll Learn in This Lesson
- Defining an interface with abc.ABC and @abstractmethod
- Why instantiating an incomplete subclass raises TypeError
- Mixing abstract methods with shared concrete methods
- Registering virtual subclasses with .register()
- When to choose an ABC over a typing.Protocol
- Using the ready-made ABCs in collections.abc
1 Defining an Interface with ABC
Inherit from abc.ABC and decorate the methods you want to require with @abstractmethod. The base class becomes a contract: it describes what subclasses must do, without saying how.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
... # body is intentionally empty
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self): # concrete: fills in the contract
return 3.14159 * self.r ** 2
c = Circle(2)
print(c.area()) # 12.56636📐 The two parts of an ABC:
class Shape(ABC): <- inherit from ABC to make it abstract
@abstractmethod <- this method MUST be overridden
def area(self): ...The ABC base flips on the machinery; each @abstractmethod adds one more requirement subclasses must satisfy.
🚫 Enforcement: TypeError on Instantiation
Here's the payoff. You cannot create an instance of the ABC itself, and you cannot create an instance of any subclass that forgot to implement an abstract method. Python catches it instantly:
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount): ...
@abstractmethod
def refund(self, amount): ...
class BrokenWallet(PaymentMethod):
def pay(self, amount):
return "paid"
# oops: forgot refund()
try:
w = BrokenWallet()
except TypeError as e:
print("Error:", e)
# Python 3.12+: Error: Can't instantiate abstract class BrokenWallet
# without an implementation for abstract method 'refund'
# Python 3.11 : Error: Can't instantiate abstract class BrokenWallet
# with abstract method refund
# The wording changed in 3.12; the TypeError itself did not.Without the ABC, BrokenWallet() would succeed and only explode later when something called .refund(). The ABC moves the failure to the earliest, clearest possible moment.
2 Abstract + Concrete Methods Together
An ABC isn't all-or-nothing. It can also provide concrete methods that build on the abstract ones — giving subclasses shared behavior for free while still demanding they fill in the gaps.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
...
def describe(self): # concrete, uses area()
return f"{type(self).__name__} area = {self.area():.2f}"
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
print(Square(3).describe()) # Square area = 9.003 Registering Virtual Subclasses
Sometimes a class already behaves like your interface but you can't (or don't want to) make it inherit from the ABC — maybe it's third-party code. .register() tells Python "treat this class as a subclass" for isinstance / issubclass checks, without changing its inheritance:
from abc import ABC, abstractmethod
class Drawable(ABC):
@abstractmethod
def draw(self): ...
class Sprite: # NOT inheriting from Drawable
def draw(self):
return "sprite!"
Drawable.register(Sprite) # adopt it as a virtual subclass
print(issubclass(Sprite, Drawable)) # True
print(isinstance(Sprite(), Drawable)) # True🧩 ABC vs Protocol & collections.abc
collections.abc — interfaces that already exist
The standard library ships ABCs for container behaviors. Anything with a __len__ is a Sized; anything iterable is an Iterable — no inheritance required, because these ABCs check for the methods:
from collections.abc import Sized, Iterable
class Box:
def __len__(self):
return 3
print(isinstance(Box(), Sized)) # True
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance(42, Iterable)) # FalseA Protocol matches any class with the right methods — no inheritance, checked by type checkers (structural typing). An ABC requires explicit inheritance or registration (nominal typing) but enforces the contract at runtime:
from typing import Protocol
class Renderer(Protocol):
def render(self) -> str: ...
class Html: # matches WITHOUT inheriting
def render(self) -> str:
return "<p>hi</p>"
def show(r: Renderer) -> str:
return r.render()
print(show(Html())) # <p>hi</p>🎯 Your Turn: Fill in the Blank
Complete the code so Dog properly implements the Animal interface. Replace each ___ with the correct token, then run it.
# 🎯 YOUR TURN: replace each ___ to make the interface work
from abc import ABC, ___ # need ABC and the decorator
class Animal(___): # make this class abstract
@___ # mark speak() as required
def speak(self):
...
class Dog(Animal):
def ___(self): # the method Dog must implement
return "Woof!"
print(Dog().speak())
# Expected output:
# Woof!from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
...
class Dog(Animal):
def speak(self):
return "Woof!"
print(Dog().speak()) # Woof!Common Errors
❌ Forgetting to inherit from ABC
class Shape: # plain class, NOT abstract
@abstractmethod
def area(self): ...
Shape() # succeeds! No enforcement at all✅ Inherit from ABC (or set metaclass=ABCMeta) — @abstractmethod alone does nothing without it.
class Shape(ABC):
@abstractmethod
def area(self): ...❌ Spelling the override wrong
class Circle(Shape):
def aera(self): # typo! 'area' is still abstract
return 1
Circle() # TypeError: abstract method area✅ Match the abstract method name exactly. The ABC only counts a requirement as met when the names line up.
❌ Expecting register() to verify methods
Drawable.register(int) # int has no draw()!
print(isinstance(5, Drawable)) # True (but 5.draw() fails)✅ Only register classes that genuinely satisfy the interface — registration is an unchecked promise.
🎯 Mini Challenge: A Notifier Hierarchy
Build an abstract Notifier with an abstract send(message) and a concrete announce() that prefixes the channel name. Then create two concrete notifiers.
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def send(self, message):
...
def announce(self, message): # concrete helper
return f"[{type(self).__name__}] {self.send(message)}"
class EmailNotifier(Notifier):
def send(self, message):
return f"emailing: {message}"
class SmsNotifier(Notifier):
def send(self, message):
return f"texting: {message}"
for n in (EmailNotifier(), SmsNotifier()):
print(n.announce("Build passed"))
# ✅ Expected output:
# [EmailNotifier] emailing: Build passed
# [SmsNotifier] texting: Build passed📚 Keep Learning
- abc — Abstract Base Classes (official docs) — the ABC class, ABCMeta, @abstractmethod, and register().
- collections.abc — ABCs for containers — ready-made Iterable, Sized, Mapping, Sequence and friends.
- typing.Protocol — structural subtyping — the duck-typed alternative to nominal ABCs.
Frequently Asked Questions
Lesson complete — you can design enforced interfaces!
You can declare an interface with abc.ABC and @abstractmethod, rely on the TypeError to catch incomplete subclasses, mix abstract and concrete methods, adopt virtual subclasses with .register(), and choose between an ABC and a Protocol. That's the foundation of robust object-oriented design.
Practice quiz
How do you make a class abstract so it can't be instantiated directly?
- Add @staticmethod to it
- Use the @abstract decorator on the class
- Inherit from abc.ABC
- Set abstract = True
Answer: Inherit from abc.ABC. A class becomes abstract by inheriting from abc.ABC (or using metaclass=ABCMeta).
What happens when you try to instantiate a subclass that hasn't implemented an @abstractmethod?
- Python raises a TypeError
- It works but the method returns None
- It raises NotImplementedError
- Python prints a warning and continues
Answer: Python raises a TypeError. Instantiating a class with unimplemented abstract methods raises TypeError immediately.
What does @abstractmethod do on its own, in a plain class that does NOT inherit from ABC?
- Makes the class abstract anyway
- Raises a SyntaxError
- Automatically adds an __enter__ method
- Nothing — without ABC there is no enforcement
Answer: Nothing — without ABC there is no enforcement. @abstractmethod alone does nothing; the class must inherit from ABC for enforcement.
Can an ABC contain concrete (fully implemented) methods alongside abstract ones?
- No, ABCs may only contain abstract methods
- Yes, and subclasses inherit the concrete methods as-is
- Yes, but subclasses must re-implement them all
- Only if they are static methods
Answer: Yes, and subclasses inherit the concrete methods as-is. ABCs can mix abstract and concrete methods; subclasses inherit concrete ones (the template-method pattern).
What does Drawable.register(Sprite) do?
- Makes Sprite a virtual subclass for isinstance/issubclass checks
- Verifies Sprite has all required methods
- Copies Drawable's concrete methods into Sprite
- Raises an error if Sprite lacks draw()
Answer: Makes Sprite a virtual subclass for isinstance/issubclass checks. register() makes a class a virtual subclass for isinstance/issubclass without checking or inheriting methods.
Does register() verify that the registered class actually has the required methods?
- Yes, it checks every abstract method
- Only in Python 3.11+
- No, registration is an unchecked promise
- Only for collections.abc classes
Answer: No, registration is an unchecked promise. Registration is a promise, not a check — Python trusts you that the class satisfies the interface.
Which typing approach matches any class with the right methods, with NO inheritance required?
- abc.ABC
- typing.Protocol (structural typing)
- ABCMeta
- collections.abc.Sized
Answer: typing.Protocol (structural typing). typing.Protocol uses structural typing — any class with the right methods matches without inheriting.
What kind of typing does an ABC use?
- Structural typing
- Duck typing only
- No typing at all
- Nominal typing — needs explicit inheritance or registration
Answer: Nominal typing — needs explicit inheritance or registration. ABCs use nominal typing: a class is a subtype only if it inherits from or is registered with the ABC.
Given a class Box with only a __len__ method, what does isinstance(Box(), Sized) return?
- False
- True
- It raises TypeError
- None
Answer: True. collections.abc.Sized matches anything with __len__, so isinstance returns True.
When should you reach for an ABC instead of a Protocol?
- When you want to avoid any inheritance
- Only for static type checking
- When you want runtime enforcement and shared base logic
- When the class is third-party code you can't modify
Answer: When you want runtime enforcement and shared base logic. Choose an ABC for runtime enforcement, shared base logic, or an explicit hierarchy.
Continue this course
- Previous: Checkpoint: Data Structures
- Next: Properties & Descriptors — Computed, validated attributes with @property and the descriptor protocol
- Quick reference: Python cheat sheet
Frequently asked questions
What is an abstract base class in Python?
An abstract base class (ABC) is a class that defines a set of methods subclasses must implement but that cannot be instantiated on its own. You build one by inheriting from abc.ABC and marking methods with @abstractmethod. It lets you declare an interface — a contract — and have Python enforce it at instantiation time instead of letting bugs slip through.
Why can't I create an instance of my ABC?
If a class still has unimplemented @abstractmethod methods, Python raises TypeError when you try to instantiate it. That is the whole point: the ABC is incomplete until a subclass fills in every abstract method. Once a subclass implements all of them, that subclass becomes concrete and instantiable.
What's the difference between an ABC and a Protocol?
An ABC uses nominal typing — a class is a subtype only if it explicitly inherits from (or is registered with) the ABC. A typing.Protocol uses structural typing — any class with the right methods matches, no inheritance needed (duck typing checked by type checkers). Use an ABC when you want runtime enforcement and shared base logic; use a Protocol when you only want static, inheritance-free interface checks.
What is collections.abc and should I use it?
collections.abc is a module of ready-made ABCs for the core container behaviors — Iterable, Sized, Mapping, Sequence, and more. You can isinstance-check against them or subclass them to get mixin methods for free. They are the canonical way to ask 'does this object behave like a sequence?' rather than checking for a concrete type like list.