Python Programming from First Principles: Learn to Code, Think Computationally, and Solve Real-World Problems
PDFEPUB
BOOK 000 · CHAPTER 7 · Multiple Inheritance

Multiple Inheritance

On this pageExample: Teaching Assistant

A class can inherit from more than one base. Python uses a method resolution order (MRO) to decide which implementation a lookup finds.

The UML hollow triangles show the two base classes. Python’s lookup order is stated in the note; it is not encoded by the generalization lines themselves.

DerivedBase1+greet()Base2+greet()Python base order: Base1, Base2
class Base1:
    def greet(self):
        return "Hello from Base1"

class Base2:
    def greet(self):
        return "Hello from Base2"

class Derived(Base1, Base2):
    pass

d = Derived()
print(d.greet())
Listing 343 — Multiple Inheritance

Output

Hello from Base1

Base1 precedes Base2, so its greet wins. Python calculates a C3 ordering, not a simple depth-first traversal; inspect Derived.__mro__ when needed.

Example: Teaching Assistant

The teaching assistant combines Student behavior with Employee information. Read the definitions, then run the example below.

The UML generalization triangles point to base classes. In this implementation, Student inherits from User, while Employee is a separate base.

TeachingAssistant+assist(course_code)+role()Student+courses+enroll(course_code)Employee+employee_id+pay_info()User+profile()
class User:
    def __init__(self, user_id, name, email):
        self.user_id = str(user_id)
        self.name = name
        self.email = email

    def profile(self):
        return f"{self.name} <{self.email}>"

    def role(self):
        return "user"   # default label

    def describe(self):
        return f"{self.role().title()}: {self.profile()}"


class Student(User):
    def __init__(self, user_id, name, email):
        super().__init__(user_id, name, email)
        self.courses = []  # student-specific attribute

    def role(self):
        return "student"   # override the parent's version

    def enroll(self, course_code):
        if course_code not in self.courses:
            self.courses.append(course_code)

    def describe(self):
        base = super().describe()  # reuse User's describe() output
        return f"{base} — enrolled in {len(self.courses)} course(s)"


class Professor(User):
    def __init__(self, user_id, name, email):
        super().__init__(user_id, name, email)
        self.teaches = []

    def role(self):
        return "professor"   # override parent's version

    def assign_course(self, course_code):
        if course_code not in self.teaches:
            self.teaches.append(course_code)

    def describe(self):
        base = super().describe()
        return f"{base} — teaches {len(self.teaches)} course(s)"


class Employee:
    def __init__(self, employee_id):
        self.employee_id = employee_id

    def pay_info(self):
        return f"Employee #{self.employee_id}"


class TeachingAssistant(Student, Employee):
    def __init__(self, user_id, name, email, employee_id):
        Student.__init__(self, user_id, name, email)   # initialize Student part
        Employee.__init__(self, employee_id)           # initialize Employee part
        self.assists = []

    def role(self):
        return "teaching assistant"

    def assist(self, course_code):
        if course_code not in self.assists:
            self.assists.append(course_code)
Listing 344 — Example: Teaching Assistant

Defines User, Student, Professor, Employee, TeachingAssistant; no visible output.

Student initializes User; TeachingAssistant separately initializes Employee. This works for this specific hierarchy. Do not generalize explicit base calls to cooperative diamond inheritance, where a shared base might otherwise be initialized twice.

ta = TeachingAssistant("S-010", "Alan Turing", "alan@example.org", "E-500")
ta.enroll("CS101")
ta.assist("CS101")

print(ta.describe())   # from Student/User
print(ta.pay_info())   # from Employee
Listing 345 — Example: Teaching Assistant

Output

Teaching Assistant: Alan Turing <alan@example.org> — enrolled in 1 course(s)
Employee #E-500

The inherited description uses the overridden role; pay_info reads the employee identifier.

Try it: reverse Base1 and Base2 in the first example. Predict the greeting, run it, and inspect the MRO. Prefer a simpler composition design when separate roles do not need shared inheritance.

With these relationships established in code, we can use diagrams to make an object-oriented design easier to inspect.