2.1.13OOP Fundamentals

Abstract base classes — ABC module, `@abstractmethod`

1,942 words9 min readdifficulty · medium2 backlinks

WHAT is an ABC?


WHY do we need them?

Two big wins:

  1. Fail early, fail loud — missing methods are caught at instantiation, not later.
  2. Self-documenting design — the ABC lists exactly what a subclass owes you.

HOW it works (derivation from first principles)

We don't need to memorise magic — we can build up the behaviour.

Step 1 — A plain base class does NOT enforce anything.

class Shape:
    def area(self):
        raise NotImplementedError   # only fails when CALLED

Why this step? This is the "poor man's ABC". The error only fires when someone actually calls area(). You can still create Shape() and pass it around — the bomb is hidden.

Step 2 — Inherit from ABC + mark the method abstract.

from abc import ABC, abstractmethod
 
class Shape(ABC):
    @abstractmethod
    def area(self):
        ...

Why this step? ABC uses a special metaclass (ABCMeta). When you try Shape(), the metaclass checks an internal set called __abstractmethods__. If it is non-empty, instantiation is blocked immediately.

Step 3 — Watch the enforcement.

Shape()        # TypeError: Can't instantiate abstract class Shape
               # with abstract method area

Why this step? The error now happens at construction time and names the missing method. That's the whole point.

Step 4 — A concrete subclass clears the contract.

class Circle(Shape):
    def __init__(self, r):
        self.r = r
    def area(self):
        return 3.14159 * self.r ** 2
 
Circle(2).area()   # 12.566  ✅ works

Why this step? Once Circle overrides area, its __abstractmethods__ set is empty → instantiation allowed.

Figure — Abstract base classes — ABC module, `@abstractmethod`

The mechanism in one sentence

__abstractmethods__ is a frozenset computed automatically by ABCMeta every time a class is defined. Inherited-but-unimplemented abstract methods stay in the set; overridden ones are removed.


Going deeper: combining decorators


Worked example: a payment system

from abc import ABC, abstractmethod
 
class PaymentProcessor(ABC):
    @abstractmethod
    def pay(self, amount): ...
    @abstractmethod
    def refund(self, amount): ...
 
class CardProcessor(PaymentProcessor):
    def pay(self, amount):    return f"Charged {amount} to card"
    def refund(self, amount): return f"Refunded {amount} to card"

Why two abstract methods? The contract says "any processor must both pay AND refund." If CardProcessor only defines pay, __abstractmethods__ still contains refund, so CardProcessor(...) raises TypeError. Step-by-step why: the metaclass subtracts implemented names from the inherited abstract set; refund survives → set non-empty → blocked.

def checkout(p: PaymentProcessor, amt):
    return p.pay(amt)      # safe: ANY subclass guarantees pay() exists

Why this is safe: the type hint + ABC contract means every object reaching checkout provably has pay. No defensive hasattr needed.


Common mistakes (Steel-man each)


Recall Feynman: explain to a 12-year-old

Imagine a fill-in-the-blanks worksheet for building a toy. The worksheet says: "Every robot MUST have a walk() button and a talk() button." It's not a finished robot — you can't play with the worksheet itself. Anyone who wants to make a real robot has to fill in BOTH buttons. If they forget the talk() button, the factory refuses to build their robot and tells them exactly which button is missing — before it leaves the factory, not after a kid presses it and nothing happens. The worksheet = abstract base class. The "MUST have" buttons = @abstractmethod. The factory check = __abstractmethods__ must be empty.


Forecast-then-Verify


Flashcards

What two ingredients make a class abstract in Python?
Inherit from ABC (or metaclass=ABCMeta) AND decorate ≥1 method with @abstractmethod.
When does instantiating an abstract class fail?
At construction time (TypeError), the moment you call ClassName(), not when a method is later called.
What attribute determines if a class can be instantiated?
__abstractmethods__ — a frozenset; instantiation allowed only if it's empty.
Does @abstractmethod alone (without inheriting ABC) enforce anything?
No — it's silently ignored unless the metaclass is ABCMeta.
How does a subclass satisfy the contract?
By overriding (redefining) every abstract method in its own body.
Correct decorator order for an abstract property?
@property on top, @abstractmethod directly above def (abstractmethod innermost).
Can an abstract method contain real code?
Yes — subclasses must still override but can reuse it via super().method().
Does Python's ABC check that overriding methods have matching signatures?
No — it only checks the method name is overridden; signatures are not enforced.
What error message do you get instantiating an abstract class?
"Can't instantiate abstract class X with abstract method(s) ..." naming the missing ones.

Connections

  • Inheritance and Method Overriding — ABCs rely on overriding to clear the contract.
  • Polymorphism — ABCs guarantee a common interface so polymorphic calls are safe.
  • Duck Typing vs Nominal Typing — ABCs add nominal (declared) typing on top of duck typing.
  • Interfaces in other languages — Java interface / C++ pure virtual = 0 are the analogues.
  • Metaclasses and ABCMeta — the machinery that powers __abstractmethods__.
  • Protocols (typing.Protocol) — structural alternative to nominal ABCs.

Concept Map

provides

provides

uses

marks methods in

checks

non-empty blocks

resolved by overriding

fulfills

gives

weaker alternative to

Abstract Base Class

abc module

abstractmethod decorator

ABCMeta metaclass

__abstractmethods__ set

Cannot instantiate

Concrete subclass

Enforced contract

Fail early at creation

NotImplementedError base

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, abstract base class (ABC) ek aisa class hai jise tum directly object banane ke liye use nahi kar sakte — ye sirf ek blueprint ya contract hai. Tum from abc import ABC, abstractmethod import karte ho, apne class ko ABC se inherit karwaate ho, aur jo methods har subclass ko zaroor banane chahiye unke upar @abstractmethod laga dete ho. Bas — ab Python guarantee karta hai ki koi bhi object tab tak nahi banega jab tak saare abstract methods override na ho jaayein.

Iska faayda kya hai? Maan lo tum ek function likh rahe ho jo har Shape pe area() call karta hai. Agar kisi ne Triangle banaya aur area() likhna bhool gaya, to bina ABC ke tumhe error bahut baad mein milega — runtime pe, andar kahin, confusing AttributeError ke saath. ABC ke saath ye error object banate hi turant aa jaata hai, saaf-saaf bata kar ki "bhai, area method missing hai." Matlab bug aage aa gaya, jaldi pakda gaya — yahi hai fail early, fail loud.

Andar ka jugaad simple hai: ABCMeta ek set rakhta hai __abstractmethods__. Jab tak ye set empty nahi hota, class instantiate nahi hoti. Jaise hi subclass saare methods override kar deta hai, set khaali ho jaata hai aur object ban-ne lagta hai. Do galtiyaan yaad rakhna: (1) sirf @abstractmethod lagane se kuch nahi hota agar tumne ABC inherit nahi kiya, aur (2) @property ke saath use karte waqt @abstractmethod ko sabse neeche (innermost) rakhna. Mnemonic: "ABC = A Blueprint Cannot-build" — blanks bharo, tabhi build hoga.

Go deeper — visual, from zero

Test yourself — OOP Fundamentals

Connections