Course topics

By WebNest Studio

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

Python
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)
Output
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

Python
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))
Output
UPI OK, ref=UPI-001, fee=0.00
Card OK, ref=CARD-778, fee=20.00
rejected: invalid amount

Abstract properties and collections.abc

Python
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))
Output
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.