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?