Skip to Content
Course content

131: Abstract Base Classes with abc

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you've probably used inheritance to share code between classes. But there's a big difference between wanting to share behavior and wanting to enforce a specific structure. That's where Abstract Base Classes (ABCs) come in. Think of an ABC as a blueprint or a contract; it tells any subclass, "I don't care how you do it, but you MUST implement these specific methods if you want to be considered one of us."

Why can't I just raise NotImplementedError in a regular base class?

I see this a lot. You create a base class, write a method that just says raise NotImplementedError("Implement this!"), and call it a day. The problem is that you won't find out you forgot to implement that method until the code actually hits that line at runtime. In a large system, that's a disaster waiting to happen.

With the abc module, Python prevents you from even creating an instance of the subclass if it hasn't fulfilled the contract. It moves the failure from "somewhere deep in the logic" to the very moment you try to instantiate the object. It's a much safer way to build plugins or frameworks where other developers (or future you) will be adding new types of functionality.

How do I actually build one of these?

It's pretty straightforward. You inherit from ABC and use the @abstractmethod decorator. Let's imagine we're building a payment system. We have different providers—Stripe, PayPal, Crypto—and we need to guarantee that every single one of them has a process_payment method.

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount):
        pass

    @abstractmethod
    def refund_payment(self, transaction_id):
        pass

class StripeProcessor(PaymentProcessor):
    def process_payment(self, amount):
        print(f"Processing ${amount} via Stripe...")

    def refund_payment(self, transaction_id):
        print(f"Refunding {transaction_id} via Stripe...")

# This works fine
stripe = StripeProcessor()

# This would raise a TypeError because PaymentProcessor is abstract
# processor = PaymentProcessor() 

Can an abstract class have "normal" methods with actual logic?

Absolutely. This is where ABCs get powerful. You can mix abstract methods (the "must-do" list) with concrete methods (the "here is a shared tool" list). I often use this to handle logging or validation that should be identical across all subclasses, so I don't have to repeat that code everywhere.

class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount):
        pass

    # This is a concrete method. Every subclass gets this for free.
    def log_transaction(self, amount):
        print(f"LOG: Transaction of ${amount} initiated.")

class PayPalProcessor(PaymentProcessor):
    def process_payment(self, amount):
        self.log_transaction(amount) # Using the base class logic
        print(f"Processing ${amount} via PayPal...")

    def refund_payment(self, transaction_id):
        print(f"Refunding {transaction_id} via PayPal...")

What happens if I forget to implement one of the methods?

Python will throw a TypeError the moment you try to create the object. It won't let you get away with it. If PayPalProcessor forgot to implement refund_payment, Python would tell you exactly which method is missing.

I find this incredibly helpful when working in teams. Instead of writing a long README explaining what methods a new plugin needs to have, I just give them the ABC. The code becomes the documentation. If they break the contract, the interpreter tells them immediately.




📋 Practical Task

Build a Multi-Format Document Exporter

You are building a system that can export data to different file formats (PDF, HTML, and Markdown). To ensure every exporter follows the same rules, you need to implement a formal contract.

Your Task:

  • Create an abstract base class called DocumentExporter using the abc module.
  • Define two abstract methods: export(self, data) and get_default_extension(self).
  • Implement a concrete method in the base class called validate_data(self, data) that simply returns True if the data is a string and False otherwise.
  • Create two subclasses: PDFExporter and HTMLExporter.
  • Each subclass must implement the two abstract methods. In export, you should call the validate_data method from the base class; if the data is valid, print a message saying the data is being exported to that specific format.
  • Instantiate both exporters and call their export methods with a valid string.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.