Abstract Base Classes & Protocols Interview Questions & Answers
5 questions Updated 2026-06-18
Python interview questions on abstract base classes and @abstractmethod, why use ABCs, collections.abc, duck typing vs ABCs, and typing.Protocol for structural/static duck typing with runtime_checkable.
An abstract base class (ABC) is a class that can't be instantiated
directly and defines a set of methods subclasses must implement. You build
one by subclassing abc.ABC and decorating required methods with
@abstractmethod. Python refuses to instantiate any subclass that leaves an
abstract method unimplemented.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): # subclasses MUST implement this
...
Shape() # TypeError — can't instantiate abstract class
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self): # concrete implementation
return 3.14159 * self.r ** 2
Circle(2).area() # 12.566... — works
@abstractmethod turns the "must implement" contract into an enforced,
load-time check rather than a runtime NotImplementedError later. It defines
an interface that subclasses are required to fulfill.
ABCs enforce an interface — they guarantee at instantiation time that
subclasses provide the required methods, catching mistakes early instead of
blowing up deep in your code. They also document intent clearly and integrate
with isinstance checks, so callers can verify capabilities.
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, key, value): ...
@abstractmethod
def load(self, key): ...
class FileStorage(Storage):
def save(self, key, value): ...
# forgot load() ...
FileStorage() # TypeError — flags the missing method immediately
isinstance(FileStorage, type) and issubclass(FileStorage, Storage) # True
Use an ABC when you have a family of classes that must share a contract and
you want that contract enforced. For looser, optional interfaces, plain duck
typing or a Protocol may fit better.
collections.abc provides the standard ABCs for Python's container
protocols — Iterable, Iterator, Sequence, Mapping, Hashable, and
more. You use them two ways: as a base class to inherit mixin methods, and
in isinstance checks to test whether an object supports a protocol.
from collections.abc import Iterable, Sequence, Mapping
isinstance([1, 2], Iterable) # True
isinstance("abc", Sequence) # True
isinstance({}, Mapping) # True
class MyList(Sequence): # inherit the protocol's mixin methods
def __init__(self, data):
self._data = data
def __getitem__(self, i):
return self._data[i]
def __len__(self):
return len(self._data)
# get __contains__, __iter__, __reversed__, index, count for FREE
Subclassing Sequence and implementing just __getitem__ + __len__ gives you
a fully functional sequence. Prefer these standard ABCs over inventing your own
for container-like types.
Duck typing is "if it walks like a duck and quacks like a duck, it's a duck" — Python doesn't check the type, it just tries the operation and works if the needed methods exist. ABCs add an explicit, enforced contract on top, letting you assert membership and catch missing methods up front.
# Duck typing — no declared interface, just call and hope:
def make_it_quack(thing):
thing.quack() # works for ANYTHING with a quack() method
class Dog:
def quack(self): return "woof-quack"
make_it_quack(Dog()) # works — Dog "is" a duck here
# ABC — explicit, checkable contract:
from abc import ABC, abstractmethod
class Duck(ABC):
@abstractmethod
def quack(self): ...
Duck typing is flexible and Pythonic but defers errors to runtime; ABCs trade some flexibility for early enforcement and clear interfaces. Use duck typing for loose coupling, ABCs when you want guarantees.
typing.Protocol enables structural typing ("static duck typing"): a class
satisfies a protocol simply by having the right methods/attributes — no
explicit inheritance needed. Type checkers verify the structure statically,
giving you duck typing's flexibility with static safety. Add
@runtime_checkable to also allow isinstance checks at runtime.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Drawable(Protocol):
def draw(self) -> str: ... # required shape, not inheritance
class Button: # does NOT inherit Drawable
def draw(self) -> str:
return "[Button]"
def render(item: Drawable) -> str: # type checker accepts Button
return item.draw()
render(Button()) # works — structural match
isinstance(Button(), Drawable) # True — thanks to @runtime_checkable
Unlike ABCs, classes don't register or subclass anything — matching the
structure is enough. Note @runtime_checkable only checks method
names/existence, not signatures. Use Protocols for flexible, statically
verified interfaces; use ABCs when you need shared implementation or explicit
registration.
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.