Python Tutorial
Abstraction and Abstract Base Classes
Abstraction means exposing what an object does while hiding how it does it. You call storage.save(file) without caring whether it writes to disk, S3 or a database. In Python, abstraction is formalised with abstract base classes (ABCs) from the abc module: a base class declares methods that every subclass must implement, and Python refuses to create objects of incomplete subclasses.
This lesson covers ABC and @abstractmethod, abstract properties, template methods, and the ready-made ABCs in collections.abc.
Defining an Abstract Base Class
Inherit from abc.ABC and mark required methods with @abstractmethod. The ABC itself cannot be instantiated, and neither can any subclass that fails to implement all abstract methods — you get a TypeError at creation time instead of a confusing error later. Abstract methods may still contain a default implementation that subclasses call via super().
The Template Method Pattern
An ABC often combines concrete and abstract methods: a concrete process() defines the overall algorithm (validate → charge → notify) and calls abstract steps that each subclass fills in. This keeps the workflow consistent while allowing variation in the details.
collections.abc
The standard library defines ABCs for common protocols: Iterable, Sequence, Mapping, MutableMapping, Callable, Sized... Inheriting from one gives you mixin methods for free — implement __getitem__ and __len__ in a Sequence subclass and you get __contains__, __iter__, index and count. They are also useful in isinstance checks.
Examples
An abstract base class and concrete implementations
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, name, data):
"""Persist data under a name."""
@abstractmethod
def load(self, name):
"""Return previously saved data."""
class MemoryStorage(Storage):
def __init__(self):
self._files = {}
def save(self, name, data):
self._files[name] = data
def load(self, name):
return self._files[name]
class IncompleteStorage(Storage):
def save(self, name, data):
pass
store = MemoryStorage()
store.save("notes.txt", "Learn ABCs")
print(store.load("notes.txt"))
for cls in (Storage, IncompleteStorage):
try:
cls()
except TypeError as e:
print("TypeError:", e)
Learn ABCs
TypeError: Can't instantiate abstract class Storage without an implementation for abstract methods 'load', 'save'
TypeError: Can't instantiate abstract class IncompleteStorage without an implementation for abstract method 'load'
Template method: shared workflow, varying steps
from abc import ABC, abstractmethod
class PaymentProcessor(ABC):
def process(self, amount): # concrete template method
if amount <= 0:
return "rejected: invalid amount"
ref = self.charge(amount)
return f"{self.name()} OK, ref={ref}, fee={self.fee(amount):.2f}"
@abstractmethod
def charge(self, amount): ...
@abstractmethod
def name(self): ...
def fee(self, amount): # default hook, may be overridden
return 0.0
class UpiProcessor(PaymentProcessor):
def charge(self, amount):
return "UPI-001"
def name(self):
return "UPI"
class CardProcessor(PaymentProcessor):
def charge(self, amount):
return "CARD-778"
def name(self):
return "Card"
def fee(self, amount):
return amount * 0.02
for p in (UpiProcessor(), CardProcessor()):
print(p.process(1000))
print(UpiProcessor().process(0))
UPI OK, ref=UPI-001, fee=0.00
Card OK, ref=CARD-778, fee=20.00
rejected: invalid amount
Abstract properties and collections.abc
from abc import ABC, abstractmethod
from collections.abc import Sequence, Mapping
class Shape(ABC):
@property
@abstractmethod
def sides(self): ...
class Triangle(Shape):
@property
def sides(self):
return 3
print(Triangle().sides)
class Deck(Sequence):
def __init__(self):
self._cards = [f"{r}{s}" for s in "♠♥" for r in "AKQ"]
def __getitem__(self, i):
return self._cards[i]
def __len__(self):
return len(self._cards)
deck = Deck()
print(len(deck), deck[0], "Q♥" in deck, deck.index("K♥"), list(reversed(deck))[:2])
print(isinstance({}, Mapping), isinstance(deck, Sequence))
3
6 A♠ True 4 ['Q♥', 'K♥']
True True
Common Mistakes
- Forgetting to inherit from ABC, so @abstractmethod is not enforced.
- Creating ABCs for every class "just in case" — use them where several implementations really exist.
- Implementing abstract methods with different signatures in subclasses.
- Using ABCs where a typing.Protocol (structural typing) would avoid forced inheritance.
Key Points to Remember
- Abstraction hides implementation behind a clear interface.
- ABC + @abstractmethod prevent instantiation of incomplete classes.
- Template methods keep a fixed workflow with overridable steps.
- collections.abc provides ready-made ABCs with free mixin methods.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.