← Back to list

Abstract Base Classes vs. Protocols in Python: A Real-World Perspective

Why You Need to Know This

Yash Jain in AlgoMart · 2025-06-18 04:32 · 36 claps · 4.0 min read paywalled
#python #abstract-base-classes #protocol #python-abstract-classes #python-class
Open on Medium ↗
Wiki topics: TLS · Design Tools & Workflow

Abstract Base Classes vs. Protocols in Python: A Real-World Perspective

Blog Thumbnail

Blog Thumbnail

Why You Need to Know This

When you’re developing serious Python applications — the kind that stick around, get handed over, and need to be maintained — your code needs agreement. Agreement between developers, components, and interfaces.

Unlike statically typed languages, Python runs on the assumption of “if it looks like it, then it must be”. This works fine — until it doesn’t. Bugs show up weeks later when the code gets reused or modified.

Imagine you’re writing a storage layer that expects an object to have a .save() method. You assume every implementor will write it. But someone forgets, and now the system breaks at runtime.

To prevent these issues before they happen, Python offers two methods to define and enforce behavior across different parts of a codebase:

  1. Abstract Base Classes (ABCs)
  2. Protocols

Both help with defining common patterns. But they do it differently. Let’s walk through each, from scratch — not with jargon, but from the angle of how a typical developer would use them on the job.

What Are Abstract Base Classes?

Let’s say you’re writing a reporting system. Each report type should have a method called generate(). Some generate HTML, others PDF.

You can declare an abstract class for this behavior:

from abc import ABC, abstractmethod
class Report(ABC):
    @abstractmethod
    def generate(self) -> str:
        pass

Now when your teammate writes this:

class PDFReport(Report):
    pass

Python throws an error — it doesn’t let you create an instance unless the subclass actually implements generate().

This is good. It’s not guesswork. Python checks it for you.

You can also provide shared methods across subclasses:

class Report(ABC):
    @abstractmethod
    def generate(self) -> str:
        pass
    def save(self, path: str):
        content = self.generate()
        with open(path, 'w') as f:
            f.write(content)

Every subclass will inherit the save() method, but still must implement generate().

What Problem Do ABCs Solve?

  • Your interface has required methods.
  • You want to enforce that at runtime.
  • You need shared behavior across children.

Downsides?

  • They rely on inheritance. Classes must explicitly say “I’m a Report”.
  • You can’t easily apply them to third-party classes you don’t control — unless you register them manually.
  • Little flexibility if your objects don’t neatly align to a hierarchy.

What Are Protocols?

Protocols aim to solve the same problem — but differently.

Rather than enforcing a parent-child relationship, they just care if an object “has the required stuff”. Think “looks like a duck”.

Here’s the same example, using Protocols:

from typing import Protocol
class Report(Protocol):
    def generate(self) -> str:
        ...

Now, if you write a class that has a method called generate returning a string, it “matches” the protocol — even if it never heard of Protocol.

class PDFReport:
    def generate(self) -> str:
        return "<PDF content>"

This class fits the Report protocol. And if you type hint your functions or methods to accept a Report, tools like Mypy will confirm that it’s acceptable.

Strengths of Protocols:

  • No inheritance needed.
  • Objects are accepted based on behavior, not ancestry.
  • Works well with libraries or plugins that don’t inherit from your base class.

Where Protocols Struggle:

  • You don’t get runtime enforcement unless decorated specifically.
  • isinstance() checks don’t work unless you make them runtime-checkable.

You can fix the last part like this:

from typing import runtime_checkable
@runtime_checkable
class Report(Protocol):
    def generate(self) -> str:
        ...

Then you can use isinstance(pdf_report, Report) at runtime.

How Protocols and ABCs Differ

You might be thinking: “Both define methods. What’s the real difference?”

Here’s a direct compare:

Abstract Base Class vs Protocol

Abstract Base Class vs Protocol

In short:

  • ABCs = stricter, better for code you control
  • Protocols = looser, better for plugins, integrations, or flexible APIs

When You Should Pick One Over the Other

Use an Abstract Base Class when:

  • Your system has multiple components that must have the same core logic or behavior.
  • You want to force a developer (or yourself) to implement certain methods.
  • You’re creating internal APIs or base libraries where you control all subclasses.

Use a Protocol when:

  • You’re integrating code you don’t own.
  • You care about method shapes, not inheritance.
  • You want flexibility in testing, mocking, or plugin design.

Example: Mixed Use Case

Let’s say you’re building a data exporter system. You want to enforce that coders provide an export() method. But you also want to accept objects from other packages that already have the right method.

Here’s how you combine them.

Step 1: Define an ABC for internal use:

from abc import ABC, abstractmethod
class DataExporter(ABC):
    @abstractmethod
    def export(self) -> str:
        pass

Step 2: Also define a Protocol:

from typing import Protocol
class Exportable(Protocol):
    def export(self) -> str:
        ...

Step 3: Accept any object matching the Protocol:

def handle_export(item: Exportable):
    print(item.export())

Step 4: Allow your internal classes to extend the ABC:

class JSONExporter(DataExporter):
    def export(self) -> str:
        return '{"items": []}'

JSONExporter satisfies both the ABC and the protocol. At the same time, outside classes can be accepted — as long as they have an export() method.

Bottom Line

Use Abstract Base Classes for structure and enforcement. Use Protocols for flexibility and behavior-based typing.

It’s not ABC vs. Protocol. It’s ABC and Protocol — depending on what you’re building.

If your system needs strict behavior enforcement, go with ABCs. If your system needs loose, plugin-friendly architecture, go with Protocols.

Teams that understand this distinction tend to write clearer contracts, catch bugs earlier (especially with Mypy), and design software that’s easier to test, extend, and maintain.

No magic. Just proper tools used correctly. Python gives you both. Knowing when to reach for each is what separates quick scripts from large, dependable systems.

Feel free to leave a comment on this blog or reach out to me on

Topmate: https://topmate.io/yash0307jain

Or connect with me on LinkedIn

Linkedin: https://www.linkedin.com/in/yash0307jain/

Thanks for reading, and I’ll see you next time!


메타데이터
post_id
bdc85b74f48c
slug
abstract-base-classes-vs-protocols-in-python-a-real-world-perspective-bdc85b74f48c
url
https://medium.com/algomart/abstract-base-classes-vs-protocols-in-python-a-real-world-perspective-bdc85b74f48c
canonical_url
https://medium.com/algomart/abstract-base-classes-vs-protocols-in-python-a-real-world-perspective-bdc85b74f48c
author_url
https://medium.com/@yashjaincodex
status
ok
fetched_at
2026-06-09 14:34:10