Python 3.14 ·
✓ verified by execution on 2026-07-29
Python is famous for its dynamic “duck typing” — if it walks like a duck and quacks like a duck, it’s a duck! But as Python codebases grow, relying purely on runtime behavior can lead to unpredictable errors.
To solve this, Python gives us two powerful ways to define and enforce interfaces: Abstract Base Classes (ABCs) for nominal typing, and Protocols for structural subtyping.
Let’s explore how they differ and when to use each.
Abstract Base Classes (ABCs)
The abc module provides the infrastructure for defining abstract base classes (ABCs) in Python.
An ABC allows you to define an interface that other classes must implement. You can define an abstract method using the @abstractmethod decorator.
What happens if a subclass forgets to implement an abstract method? A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.
python
from abc import ABC, abstractmethodclass Shape(ABC): @abstractmethod def area(self): passtry: s = Shape()except TypeError as e: print(str(e))
Output
Can't instantiate abstract class Shape without an implementation for abstract method 'area'
Your output
Misconception: Abstract methods cannot have an implementation
An abstract method can have an implementation (which can be called using super() by subclasses), but subclasses are still forced to override it.
While ABCs require explicit inheritance (nominal typing), Protocols allow you to define structural subtyping (static duck-typing).
Introduced in Python 3.8 via the typing module, such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).
A class is considered a subtype of a Protocol if it implements all the protocol’s methods, even without inheriting from it.
Notice that Bird never explicitly inherits from SupportsFly. The type checker simply looks at the structure: does Bird have a fly() method? Yes!
When to use Protocols?
Protocols are perfect when you want to type-check objects that you didn’t write yourself (like standard library classes or third-party objects) that already implement the methods you need, but don’t inherit from your ABC.
Checking Interfaces at Runtime
By default, Protocols are only for static type checking (like mypy). If you try to use isinstance() with a regular Protocol, it will raise a TypeError.
To use isinstance() with Protocols at runtime, you must decorate the Protocol with @runtime_checkable.
Use Abstract Base Classes when you own the class hierarchy and want to strictly enforce method implementation at instantiation time.
Use Protocols when you want to define interfaces based on what objects do (duck typing), especially for objects you don’t control.
Check yourself
Your function needs any object with a .read() method. Which approach avoids forcing callers to inherit from your class?
Reveal answer
A Protocol — structural subtyping matches on methods, not ancestry — Protocols use structural subtyping: any class with the right methods satisfies it, with no inheritance relationship required.
Can an @abstractmethod contain a real implementation?
Reveal answer
Yes, and subclasses are still required to override it — An abstract method may carry a usable implementation (reachable via super()), but the subclass is still forced to override it.
What does inheriting from an ABC actually do at runtime?
Reveal answer
Refuses instantiation until every abstract method is implemented — ABCs are a design and type-checking tool. The runtime effect is blocking instantiation of an incomplete subclass, not performance.
Challenges
Challenge 1 +50 XP
Create an Abstract Base Class named `Vehicle` with an abstract method `start()`. Then create a `Car` subclass that implements `start()` by returning 'Vroom'. Instantiate a `Car` and print the result of `start()`.
python
Test 1 — expects "Vroom"
Need a hint? (−25% XP)
Use the ABC class and the @abstractmethod decorator.
Define a `Drawable` Protocol that specifies a `draw(self) -> str` method. Decorate it with `@runtime_checkable`. Create a `Circle` class that implements `draw` returning 'Drawing Circle'. Print whether a `Circle` instance is an instance of `Drawable` using `isinstance()`.
python
Test 1 — expects "True"
Need a hint? (−25% XP)
Use @runtime_checkable on the Drawable Protocol so you can use isinstance().