Inheritance & the MRO Interview Questions & Answers

5 questions Updated 2026-06-18

Python interview questions on single vs multiple inheritance, the MRO and C3 linearization, super(), the diamond problem, mixins, and abstract base classes.

Single inheritance means a class derives from exactly one parent; multiple inheritance means it lists more than one base class. Python supports both — multiple inheritance is what lets you compose behaviour from several sources at once.

class Animal:
    def eat(self): print("eating")

class Dog(Animal):          # single inheritance
    def bark(self): print("woof")

class Swimmer: ...
class Flyer: ...
class Duck(Animal, Swimmer, Flyer):  # multiple inheritance
    pass

Multiple inheritance is powerful but can create ambiguity about which parent's method wins — that ambiguity is resolved by the MRO. Rule of thumb: prefer single inheritance plus small mixins over deep, wide hierarchies.

The MRO (Method Resolution Order) is the linear, ordered list of classes Python searches when looking up an attribute or method on an instance. CPython builds it with the C3 linearization algorithm, which guarantees a consistent order that respects each class's own order of bases and never places a parent before its child.

class A: ...
class B(A): ...
class C(A): ...
class D(B, C): ...

D.__mro__            # (D, B, C, A, object)
D.mro()             # same, as a list

C3 produces a single deterministic order (or raises TypeError if no consistent order exists). Attribute lookup walks this list left to right and stops at the first match. Rule of thumb: read Cls.__mro__ whenever multiple inheritance surprises you — it tells you exactly who wins.

super() does not simply call "the parent class" — it calls the next class in the instance's MRO, starting after the current class. That cooperative behaviour is what makes multiple inheritance work correctly: each class delegates to whatever comes next, regardless of the static hierarchy.

class A:
    def greet(self): print("A")
class B(A):
    def greet(self): print("B"); super().greet()
class C(A):
    def greet(self): print("C"); super().greet()
class D(B, C):
    def greet(self): print("D"); super().greet()

D().greet()   # D, B, C, A  — follows D.__mro__, not B's parent

Note super().greet() inside B calls C, not A, because the MRO of a D instance puts C after B. Rule of thumb: in a cooperative hierarchy every override should call super() so the whole chain runs exactly once.

The diamond problem arises when two classes (B, C) inherit from a common base (A), and a fourth class (D) inherits from both. The question is: when D calls an inherited method, is A's code run once or twice? Naive languages run it twice; Python's C3 MRO guarantees A appears exactly once, so cooperative super() calls run it a single time.

class A:
    def __init__(self): print("A"); 
class B(A):
    def __init__(self): print("B"); super().__init__()
class C(A):
    def __init__(self): print("C"); super().__init__()
class D(B, C):
    def __init__(self): print("D"); super().__init__()

D()   # D, B, C, A  — A's __init__ runs ONCE

The MRO (D, B, C, A, object) linearizes the diamond into a clean chain. Rule of thumb: the diamond is only safe when every class in it uses super() consistently — mixing super() with hard-coded Base.__init__(self) calls breaks the guarantee.

A mixin is a small class that provides a focused slice of behaviour meant to be combined into other classes via multiple inheritance — it isn't useful on its own and usually has no __init__. An abstract base class (ABC), from the abc module, defines an interface with @abstractmethods and cannot be instantiated until every abstract method is overridden.

from abc import ABC, abstractmethod

class JsonMixin:                 # mixin: adds one capability
    def to_json(self): import json; return json.dumps(self.__dict__)

class Shape(ABC):                # abstract base: defines a contract
    @abstractmethod
    def area(self): ...

class Circle(Shape, JsonMixin):
    def __init__(self, r): self.r = r
    def area(self): return 3.14159 * self.r ** 2

Shape()    # TypeError: can't instantiate abstract class

Use mixins to share reusable behaviour and ABCs to enforce that subclasses implement a required interface (isinstance checks also work against ABCs). Rule of thumb: mixins say "you can do this", ABCs say "you must do this".

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.