Geek Logbook

Tech sea log book

ABC in Python: What It Is, Where It Comes From, and Why It Exists

When people begin exploring object-oriented design in Python, they eventually encounter this import:

from abc import ABC, abstractmethod

That usually leads to a natural question: what does ABC mean, and why does Python need something like this in such a flexible language?

The short answer is that ABC stands for Abstract Base Class. But understanding its real purpose requires looking at the broader object-oriented tradition, the idea of class contracts, and the way Python adapts classical OOP concepts to its own design philosophy.

The origin: abstract classes in object-oriented programming

The concept of an abstract class did not originate in Python. It comes from the wider object-oriented programming tradition, where a class can serve as a conceptual base for other classes without representing a complete object on its own.

The idea is straightforward:

  • some types represent a family of behaviors
  • that family shares a common structure
  • but some operations must be implemented by subclasses

For example, a class called Animal may define that every animal must be able to make a sound, but it does not make sense to specify the exact sound in that base class. That responsibility belongs to Dog, Cat, Cow, and so on.

From a design perspective, an abstract class expresses a contract: any subclass must provide certain operations or properties.

This approach is common in languages such as Java, C++, and C#, where type hierarchies are typically more explicit and formal.

How Python adopts the idea

Python incorporates this concept through the standard library module abc, which stands for Abstract Base Classes.

With this module, you can:

  • declare abstract classes
  • mark methods as abstract
  • prevent incomplete subclasses from being instantiated

Example:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        return "Woof"

class Cat(Animal):
    def make_sound(self):
        return "Meow"

Here, Animal acts as an abstract base. It is not meant to be instantiated directly. Its purpose is to define a minimum structure that subclasses must implement.

If a subclass does not implement make_sound, Python will not allow it to be instantiated.

class Fish(Animal):
passf = Fish() # TypeError

That error is not incidental. It is the mechanism that turns a design intention into an enforceable rule.

So yes: it comes from OOP

Yes, ABC is clearly rooted in object-oriented programming.

More specifically, it is related to these core ideas:

1. Abstraction

An abstract class lets you model a general concept without committing to a concrete implementation.

2. Inheritance

Subclasses receive a base structure and complete the specific parts.

3. Polymorphism

If several subclasses implement the same contract, they can be used uniformly.

For example:

def present(animal: Animal):
print(animal.make_sound())

The function present does not need to know whether it receives a Dog or a Cat. It only needs the object to satisfy the contract defined by Animal.

The interesting tension: Python is flexible

This is where the topic becomes more interesting. Python takes the concept of abstract classes from OOP, but it does so within a language that historically favors flexibility over rigidity.

In many languages, the main way to validate behavior is through the type hierarchy. In Python, by contrast, it is very common to rely on duck typing.

The principle of duck typing is familiar:

If it walks like a duck and quacks like a duck, treat it like a duck.

That means that in Python, it often does not matter what class an object inherits from. What matters is whether it provides the methods that the code expects.

For example, a function may work with any object that has a .read() method, without requiring inheritance from a special base class.

def process_source(f):
content = f.read()
return content.upper()

That function does not care whether f is a real file, an in-memory buffer, or a test double. It only cares that the object responds to read().

Then why use ABC at all?

If Python can already work through duck typing, why does ABC exist?

Because there are cases where complete flexibility is not desirable. In particular, abstract base classes are useful when the design needs to be made explicit.

1. To express contracts formally

When a base class represents a family of possible implementations, ABC makes that contract explicit and enforceable.

2. To catch errors earlier

Without formal abstraction, an incomplete subclass may go unnoticed until it fails at runtime. With ABC, the problem appears as soon as someone tries to instantiate an invalid implementation.

3. To design frameworks or libraries

When other developers are expected to extend a hierarchy, it helps to make required methods unambiguous.

4. To improve maintainability

In medium or large systems, making structural obligations explicit reduces ambiguity and improves architectural readability.

ABC is not exactly an interface

A technical nuance is worth stating clearly. ABC is often compared to an interface in Java, but they are not the same thing.

An ABC in Python is an abstract class, not a pure interface in the classical sense.

That means it can contain:

  • abstract methods
  • concrete methods
  • attributes
  • shared logic

Example:

from abc import ABC, abstractmethodclass Logger(ABC):
@abstractmethod
def write(self, message: str) -> None:
pass def info(self, message: str) -> None:
self.write(f"[INFO] {message}")

Here, Logger requires subclasses to implement write, but it also provides a concrete reusable method, info.

That is much closer to a classical abstract class than to a strict interface.

A more realistic example

Suppose you want to model multiple storage backends.

from abc import ABC, abstractmethodclass Storage(ABC):
@abstractmethod
def save(self, data: str) -> None:
pass @abstractmethod
def load(self) -> str:
pass

Now you can define concrete implementations:

class FileStorage(Storage):
def __init__(self, path: str):
self.path = path def save(self, data: str) -> None:
with open(self.path, "w") as f:
f.write(data) def load(self) -> str:
with open(self.path, "r") as f:
return f.read()

And another implementation:

class MemoryStorage(Storage):
def __init__(self):
self._data = "" def save(self, data: str) -> None:
self._data = data def load(self) -> str:
return self._data

From the perspective of the rest of the system, both objects satisfy the same contract. That uniformity is one of the main goals of object-oriented design.

When it is useful and when it is not

ABC is useful, but it should not be used mechanically.

It is useful when:

  • you are designing a hierarchy with multiple implementations
  • you need explicit contracts
  • you want to prevent incomplete subclasses
  • you are building an internal API or a library

It is not always useful when:

  • the case is small and simple
  • duck typing is sufficient
  • the hierarchy adds no real value
  • you are merely copying patterns from more rigid languages

In Python, adding formal abstraction by habit can make a design more complicated than it needs to be.

The modern evolution: Protocols

Modern Python also offers another related tool: Protocols, from the typing module.

While ABC works through explicit inheritance, Protocol lets you express structural contracts, which are much closer to duck typing.

Conceptual example:

from typing import Protocolclass Reader(Protocol):
def read(self) -> str:
...

With that, any object implementing read() can be treated as a Reader, even if it does not inherit from that class.

This reflects an important tension in Python’s design:

  • ABC represents the more formal, nominal version of a contract
  • Protocol represents a more structural and flexible version

Both are useful. The choice depends on the problem you are solving.

Conclusion

ABC in Python is the language’s formal implementation of a classical object-oriented concept: the abstract class.

It is not an arbitrary feature and not something uniquely invented by Python. It comes from a design tradition in which some classes exist to define contracts, organize hierarchies, and delegate concrete behavior to subclasses.

What is distinctive about Python is that it introduces this idea without abandoning the language’s flexible philosophy. That is why ABC does not replace duck typing; it complements it.

In practice, understanding ABC is not only about learning a standard library module. It is about understanding a design decision:

  • when implicit behavior is enough
  • when a contract should be formalized
  • and when an object-oriented hierarchy actually improves clarity

Used with discipline, ABC can improve architecture. Used unnecessarily, it can make a Python codebase more rigid than it needs to be.

Tags: