šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Python Abstract Base Classes (ABCs)

abc.ABC and @abstractmethod — how to define a class hierarchy that Python actively refuses to instantiate incorrectly, and when this nominal-typing tool beats a Protocol.

⚔ Total XP: 0|šŸ’» python XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

What happens if you try to instantiate PaymentGateway() directly, without subclassing it?


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Abstract Base Classes let you define a required interface that Python enforces at instantiation time, not just at type-check time. This lesson covers abc.ABC and @abstractmethod, and the specific situations where that runtime enforcement is worth choosing over a Protocol.

1What ABC + @abstractmethod Actually Enforces

class PaymentGateway(ABC): with methods decorated @abstractmethod defines a class that is deliberately impossible to instantiate on its own — attempting PaymentGateway() raises TypeError: Can't instantiate abstract class PaymentGateway with abstract methods charge, refund immediately, before any of your own code even runs. This isn't a convention or a lint warning; it's enforced by ABCMeta (the metaclass behind abc.ABC, tying directly back into the Metaclasses lesson), which tracks every method decorated @abstractmethod across the class hierarchy and checks, at the exact moment of instantiation, whether every one of them has been overridden by a concrete implementation.

This is the key distinguishing feature relative to a Protocol: a Protocol violation is caught by mypy or pyright, a *static* check that runs before your code executes and can, in principle, be skipped, suppressed, or simply not run if the checker isn't wired into CI. An ABC violation is caught by the Python interpreter itself, at runtime, unconditionally — even in a codebase with no type checker configured at all, StripeGateway() missing its refund implementation fails loudly the instant something tries to construct it.

This makes ABC genuinely stronger in one specific, important sense: it guarantees the contract holds in *any* execution of the code, not just in code paths a type checker happened to analyze — valuable for library authors who can't assume every consumer runs a type checker, and for catching an incomplete implementation the moment it's used, not just when a specific missing method finally gets called.

āœ•
—
+
from abc import ABC, abstractmethod

class PaymentGateway(ABC):
    @abstractmethod
    def charge(self, amount: float) -> bool: ...

    @abstractmethod
    def refund(self, amount: float) -> bool: ...

gateway = PaymentGateway()  # TypeError: Can't instantiate abstract class
localhost:3000
Enforcement Point
StripeGateway() missing refund()
TypeError at instantiation — before any code runs

2Beyond @abstractmethod: Shared Concrete Behavior Too

An ABC isn't limited to pure interface declarations with no implementation — abstract methods can coexist with fully-implemented concrete methods on the same base class, letting you provide shared behavior alongside an enforced contract. A PaymentGateway base class could implement a concrete log_transaction(self, amount) method used by every subclass, while charge and refund remain abstract and subclass-specific — combining 'here's behavior you get for free' with 'here's behavior you must provide yourself' in one hierarchy, something a pure Protocol (which carries no implementation at all) cannot do.

This is precisely the shape of many real standard-library and framework base classes: collections.abc.MutableSequence provides concrete implementations of append, extend, pop, and several other methods *derived from* a smaller set of abstract methods (__getitem__, __setitem__, __delitem__, __len__, insert) that a subclass must actually implement — write five methods, get a dozen more for free, all while ABCMeta guarantees the five required ones are genuinely present before any instance can exist.

This 'minimal required interface, maximal free behavior' pattern is a significant part of why collections.abc is structured the way it is, and it's a design technique worth deliberately reaching for: identify the smallest set of primitive operations a concept genuinely requires, make those abstract, and implement everything else in terms of them as concrete methods on the same base class.

āœ•
—
+
class StripeGateway(PaymentGateway):
    def charge(self, amount: float) -> bool:
        return True
    # forgot to implement refund()!

StripeGateway()  # TypeError: Can't instantiate abstract class StripeGateway with abstract method refund
localhost:3000
Mixed Interface
ABC with both abstract and concrete methods
Enforced contract + shared implementation, together

3ABC vs Protocol: Choosing Nominal or Structural Typing

The deeper distinction between ABC and Protocol, beyond 'runtime-enforced vs statically-checked', is nominal versus structural typing (introduced in the Type Hints lesson). Subclassing an ABC is an explicit, deliberate act — class StripeGateway(PaymentGateway): declares a real, permanent relationship in the class hierarchy, discoverable via isinstance() and issubclass() checks that reflect actual inheritance. A Protocol requires no such declaration; any object with matching methods satisfies it, whether or not its author ever knew the Protocol existed.

This makes ABC the better fit when you genuinely want to *own* and control the hierarchy of implementations — a plugin system where every plugin must explicitly register by subclassing your Plugin base class, or a payment gateway abstraction internal to your own codebase where every implementation is written by your own team and benefits from shared concrete methods. Protocol fits better when you're describing 'anything with this shape' across code you don't control — third-party classes, standard library types, or test doubles that shouldn't need to import and inherit from your interface just to satisfy it.

Neither is universally 'better' — they solve different problems. A useful rule of thumb: reach for ABC when you want runtime enforcement and are fine requiring explicit inheritance; reach for Protocol when you want static-only checking that works across code you don't own, including code that predates your interface entirely.

āœ•
—
+
class StripeGateway(PaymentGateway):
    def charge(self, amount: float) -> bool:
        print(f"Charging ${amount} via Stripe")
        return True
    def refund(self, amount: float) -> bool:
        print(f"Refunding ${amount} via Stripe")
        return True

gateway = StripeGateway()  # works — every abstract method is implemented
localhost:3000
Design Choice
ABC: explicit, enforced, owns the hierarchy
Protocol: implicit, structural, works across foreign code

4Step-by-Step Breakdown

Protocols are checked only by your type checker. ABCs are enforced by the interpreter itself, at the moment you try to instantiate an incomplete subclass — let's see that guarantee in action.

abc.ABC + @abstractmethod defines a class that CANNOT be instantiated directly, and forces subclasses to implement every abstract method.

Checkpoint: What happens if you try to instantiate PaymentGateway() directly, without subclassing it?

  • →TypeError, immediately, because it has unimplemented abstract methods
  • →It works fine, since abstractmethod is just documentation

A subclass that forgets to implement even ONE abstract method still can't be instantiated — Python catches the gap for you.

Checkpoint: If StripeGateway implements charge() but forgets refund(), when does Python catch that mistake?

  • →Immediately, the moment you try to instantiate StripeGateway()
  • →Only when something actually tries to call the missing refund() method

A complete subclass instantiates normally and works as expected — the ABC guarantees every required method exists BEFORE any code tries to call it.

ABCs enforce a contract through inheritance; Protocols achieve a similar goal structurally — comparing them directly closes out this section.

Enforce a Real Abstract Class. Finish can_instantiate(): an ABC with an unimplemented abstractmethod can never be instantiated.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported (via server-side Python execution).

FirefoxSupported

Fully supported (via server-side Python execution).

SafariSupported

Fully supported (via server-side Python execution).

EdgeSupported

Fully supported (via server-side Python execution).

Best Practices

Use ABC when you want runtime-enforced completeness and control the whole hierarchy

A plugin system or an internal interface where every implementation is written in your own codebase benefits from ABCMeta's guarantee that no incomplete implementation can ever be instantiated, by anyone, even without a type checker in the loop.

Combine abstract methods with concrete shared methods to avoid repeating logic across implementations

Following collections.abc's pattern — a small required interface plus a larger set of methods implemented in terms of it — lets every subclass get substantial behavior for free while still guaranteeing the required primitives are present.

Frequent Bugs

THE BUG

Defining a base class intended as an interface using plain inheritance and NotImplementedError inside method bodies, instead of ABC + @abstractmethod, so an incomplete subclass only fails when the missing method is actually called — potentially much later, in production.

THE FIX

Use abc.ABC and @abstractmethod for any interface-like base class — it moves the failure from "whenever the missing method happens to be called" to "the moment an incomplete subclass is instantiated", catching the bug far earlier.

Real-World Examples

A Plugin Base Class Enforcing a Complete Implementation

A data-export plugin system needs every plugin to implement both export() and validate(), and wants incomplete plugins to fail immediately at load time rather than causing a confusing failure deep in a pipeline run.

from abc import ABC, abstractmethod

class ExportPlugin(ABC):
    @abstractmethod
    def validate(self, data: dict) -> bool: ...

    @abstractmethod
    def export(self, data: dict) -> bytes: ...

    def run(self, data: dict) -> bytes:
        if not self.validate(data):
            raise ValueError("Invalid data")
        return self.export(data)

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Using NotImplementedError inside a plain (non-ABC) base class method as an ad-hoc way to signal 'subclasses must override this', which only fails when that specific method is actually called, not at instantiation time.

# Weak: fails only if refund() happens to be called class PaymentGateway: def refund(self, amount): raise NotImplementedError # Strong: fails immediately at instantiation if incomplete from abc import ABC, abstractmethod class PaymentGateway(ABC): @abstractmethod def refund(self, amount: float) -> bool: ...

The Solution //

Use abc.ABC with @abstractmethod instead, so Python enforces completeness the moment an incomplete subclass is instantiated, rather than waiting for the specific missing method to be called at some unpredictable later point.

Lesson Glossary

[01]abc.ABC

A standard library base class (using ABCMeta) that, combined with @abstractmethod, prevents instantiation of classes with unimplemented required methods.

Code Preview
// abc.ABC context

[02]@abstractmethod

A decorator marking a method as required to be overridden by any concrete (instantiable) subclass of an ABC.

Code Preview
// @abstractmethod context

[03]Nominal typing

A typing approach (used by ABC) where a type relationship must be explicitly declared, e.g. via inheritance, rather than inferred from shape.

Code Preview
// Nominal typing context

[04]collections.abc

A standard library module of ABCs (like MutableSequence) providing minimal required interfaces with substantial concrete behavior built on top.

Code Preview
// collections.abc context

Continue Learning