Lesson 19 — Class Hierarchies
Inheritance
Python supports single and multiple inheritance. super() calls the parent without naming it, and Python's Method Resolution Order (MRO) determines the call chain for multiple inheritance.
PYTHON — Inheritance
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def introduce(self):
print(f"Hello, I am {self.name}, aged {self.age}.")
def __repr__(self):
return f"{self.__class__.__name__}(name={self.name!r})"
class Learner(Person):
def __init__(self, name: str, age: int, course: str, mark: float = 0.0):
super().__init__(name, age) # call Person.__init__
self.course = course
self.mark = mark
def introduce(self): # override
print(f"I am {self.name}, studying {self.course} at Your IT Tutor.")
def describe_progress(self):
print(f"{self.name}: {self.mark}% — {self._grade()}")
def _grade(self):
return ("Distinction" if self.mark >= 80
else "Merit" if self.mark >= 60
else "Pass" if self.mark >= 50
else "Not yet competent")
l = Learner("Thandi", 21, "Python", 74.5)
l.introduce() # overridden version
print(isinstance(l, Person)) # True
print(isinstance(l, Learner)) # True
print(issubclass(Learner, Person)) # True
Multiple Inheritance and MRO
PYTHON — Multiple inheritance
class Certifiable:
def generate_certificate(self):
print(f"Certificate awarded to {self.name}")
class Recordable:
def save_to_db(self):
print(f"Saving {self.name} to database...")
# Multiple inheritance:
class GraduateLearner(Learner, Certifiable, Recordable):
def __init__(self, name, age, course, mark, thesis):
super().__init__(name, age, course, mark) # MRO handles the chain
self.thesis = thesis
def introduce(self):
super().introduce() # calls Learner.introduce
print(f"Thesis: {self.thesis}")
g = GraduateLearner("Sipho", 24, "Python", 85.0, "ML in Agriculture")
g.introduce()
g.generate_certificate()
g.save_to_db()
# MRO — Method Resolution Order:
print(GraduateLearner.__mro__)
# (<class GraduateLearner>, <class Learner>, <class Person>,
# <class Certifiable>, <class Recordable>, <class object>)
Practice Task
Your Turn
Build: Person(name, age, introduce()), Learner(Person)(course, mark, describe_progress()), GraduateLearner(Learner)(thesis, supervisor, introduce() using super()). Create one of each, store in a list, loop and call introduce(). Add a Certifiable mixin with generate_certificate(). Demonstrate isinstance() at each level.
Common Mistakes
- Forgetting super().__init__() — parent attributes are not initialised.
- Deep multiple inheritance chains — inspect the MRO with __mro__ if method calls behave unexpectedly.
- Overriding without calling super() — you may skip important parent initialisation.
- Shadowing attributes — a child attribute with the same name as a parent's silently hides the parent's.
Professional Tip
Use super() instead of ClassName.method(self, ...) — super() works correctly with multiple inheritance and the MRO. Hardcoding the parent class name breaks when you later change the hierarchy.
Mini Quiz
What does super().__init__() do in Python?
super() returns a proxy object that delegates method calls to the next class in the MRO. super().__init__() runs the parent's __init__ to initialise inherited attributes.