Python Story

Chapter #6 – Class Relationships

inheritance, MRO, super(), mixins, ABCs, composition

6.0 Prologue

Classes rarely stand alone. This chapter examines how Python classes relate to each other: single and multiple inheritance, the Method Resolution Order, mixin patterns, abstract base classes for interface contracts, and composition as an alternative to inheritance.

6.1 Single Inheritance

A class inherits all attributes and methods of its parent. Override a method by redefining it; call the parent implementation via super(): class Animal: def __init__(self, name: str) -> None: self.name = name def speak(self) -> str: raise NotImplementedError def __repr__(self) -> str: return f"{type(self).__name__}({self.name!r})" class Dog(Animal): def speak(self) -> str: return f"{self.name} says Woof!" class GuideDog(Dog): def __init__(self, name: str, owner: str) -> None: super().__init__(name) self.owner = owner def speak(self) -> str: return super().speak() + " (guide)"

6.2 Multiple Inheritance and MRO

Python supports multiple inheritance. The Method Resolution Order (MRO) — computed by the C3 linearization algorithm — determines which class's method is called when the same name appears in multiple bases: class A: def greet(self): return "A" class B(A): def greet(self): return "B->" + super().greet() class C(A): def greet(self): return "C->" + super().greet() class D(B, C): pass print(D().greet()) # B->C->A print(D.__mro__) # (D, B, C, A, object) Inspect the MRO with ClassName.__mro__ or ClassName.mro(). Always use super() (not hard-coded parent names) to cooperate correctly with the MRO.

6.3 Mixins

A mixin is a class that provides methods for reuse without being a meaningful base type on its own. Mixins are listed as additional bases and rely on the MRO to combine cleanly: class JsonMixin: def to_json(self) -> str: import json return json.dumps(self.__dict__) class LogMixin: def log(self, msg: str) -> None: print(f"[{type(self).__name__}] {msg}") class User(JsonMixin, LogMixin): def __init__(self, name: str, email: str) -> None: self.name = name self.email = email u = User("Alice", "alice@example.com") print(u.to_json()) u.log("created")

6.4 Abstract Base Classes

The abc module provides ABC and abstractmethod to declare interface contracts that subclasses must fulfill. Instantiating a class with unimplemented abstract methods raises TypeError: from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self) -> float: ... @abstractmethod def perimeter(self) -> float: ... def describe(self) -> str: return f"area={self.area():.2f}, perim={self.perimeter():.2f}" class Circle(Shape): def __init__(self, radius: float) -> None: self.radius = radius def area(self) -> float: import math return math.pi * self.radius ** 2 def perimeter(self) -> float: import math return 2 * math.pi * self.radius The collections.abc module also defines ABCs for standard protocols: Iterable, Mapping, Sequence, Callable, etc.

6.5 Composition vs Inheritance

Inheritance models an is-a relationship; composition models a has-a relationship. Composition is often more flexible because it avoids tight coupling and deep hierarchies: class Engine: def start(self) -> str: return "vroom" class Car: def __init__(self) -> None: self._engine = Engine() # composition def start(self) -> str: return self._engine.start() A useful heuristic: prefer composition when you want to reuse behavior; prefer inheritance when you need polymorphism through a common base type.

6.6 isinstance and issubclass

isinstance(obj, T) returns True if obj is an instance of T or any subclass. issubclass(Sub, Base) checks the class hierarchy. Both accept a tuple of types: isinstance(x, (int, float)). In duck-typed code, prefer checking for the protocol (e.g., hasattr) rather than the class, unless you specifically need to guard against an exact type.

6.7 Epilogue

This chapter covered Python's inheritance model, the MRO, mixins, abstract base classes, and the composition alternative. The next chapter examines Python's generic type system through the typing module.

6.8 References

abc module — python.org
collections.abc — python.org
super() — Real Python
Inheritance vs Composition — Real Python