PEP 3119 in 90 seconds — Abstract Base Classes and the @abstractmethod gate
A class contract that fails at construction, not at runtime.
Without ABCs, "this class must implement method X" was a comment in the README. The first time someone forgot, you found out at 2am because a `Fish.speak()` call hit a non-existent method deep in a callback. PEP 3119 (Python 2.6, 2007 — long since standard) added the `abc` module: ```python from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def speak(self): ... ``` Any subclass that doesn't override `speak()` raises `TypeError` the moment you try to instantiate it. No callback chains, no surprise NoneType errors — the error fires at construction.
ABCs also power `isinstance()` for protocol-like checks. Built-in ABCs in `collections.abc` (`Iterable`, `Iterator`, `Mapping`, `Sequence`, `Sized`, `Container`, …) let you check capability instead of concrete type: ```python from collections.abc import Sequence isinstance([1, 2, 3], Sequence) # True — lists are sequences isinstance("hi", Sequence) # True — strs are sequences too ``` It's the precursor to PEP 544 (`Protocol`) for static duck-typing. ABCs check at runtime; `Protocol` checks at type-check time.
# README says "subclasses must implement speak()" but nothing enforced it.
class Animal:
def speak(self):
raise NotImplementedError("subclasses must implement speak()")
class Dog(Animal):
def speak(self):
return "woof"
class Fish(Animal):
pass # forgot
# Failure only at the FIRST call — could be deep in a callback chain
Fish().speak() # NotImplementedError, surfaces at runtimefrom abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
...
class Dog(Animal):
def speak(self):
return "woof"
class Fish(Animal):
pass # forgot — but now we catch it earlier
Dog().speak()
# Fish() ← TypeError raised HERE, at construction, not at first call
#
# Bonus: collections.abc has ready-made ABCs you can subclass or
# isinstance-check against (Iterable, Sized, Mapping, Sequence, ...)🎯 Predict the output
What does this print? `Fish` doesn't override `speak()`, so instantiating it raises `TypeError`.
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self): ...
class Dog(Animal):
def speak(self):
return "woof"
class Fish(Animal):
pass
print(Dog().speak())
try:
Fish()
print("Fish instantiated (BUG)")
except TypeError:
print("Fish: blocked — speak() not implemented")
print(issubclass(Dog, Animal))
print(isinstance(Dog(), Animal))