← Previous

Lesson 22

Inheritance

~35 min

1. Inheritance in Python

Inheritance is one of the core pillars of Object-Oriented Programming (OOP). It allows a new class (known as the child or derived class) to inherit attributes and methods from an existing class (known as the parent or base class). This promotes code reusability, reduces redundancy, and allows you to establish natural hierarchical relationships in software.

Learning Objectives

  • Understand the relationship between Parent (Base) and Child (Derived) classes.
  • Master method overriding and calling parent constructors using super().
  • Explore different forms of inheritance: Single, Multilevel, Hierarchical, and Multiple Inheritance.
  • Understand Python's Method Resolution Order (MRO) and how to inspect it.
  • Enforce structural blueprints across child classes using Abstract Base Classes (ABCs).

2. What is Inheritance?

Inheritance allows you to create a general base class containing common functionality, and then specialize it into child classes without rewriting identical logic. In Python, passing the parent class name inside parentheses during child class definition establishes the inheritance link.

Syntax:

class ParentClass:
    # Common attributes and methods
    pass

class ChildClass(ParentClass):
    # Specialized attributes and methods
    pass

Python code example:

# Parent (Base) Class
class Animal:
    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print(f"{self.name} makes a generic sound.")

# Child (Derived) Class inheriting from Animal
class Dog(Animal):
    def fetch(self):
        print(f"{self.name} is fetching the ball!")

# Instantiating Dog
my_dog = Dog("Buddy")

# Dog inherits attributes and methods from Animal automatically!
my_dog.make_sound()  # Output: Buddy makes a generic sound.
my_dog.fetch()       # Output: Buddy is fetching the ball!

3. Types of Inheritance in Python

Python supports multiple forms of inheritance structure depending on how classes interact:

Type Structure Description Code Pattern
Single A → B One child class inherits from one parent class. class B(A):
Multilevel A → B → C A child class acts as a parent for another child class. class C(B):
Hierarchical A → B, A → C Multiple child classes inherit from the same parent. class B(A): and class C(A):
Multiple (A, B) → C A single child class inherits from two or more parent classes. class C(A, B):

4. The super() Function & Method Overriding

Method Overriding happens when a child class provides its own implementation of a method that is already defined in its parent class.

When overriding constructors or methods, you frequently want to extend the parent behavior rather than completely replace it. The super() function gives you access to the parent class methods directly from within the child class.

Python code example:

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def start_engine(self):
        print(f"The engine of {self.brand} {self.model} is starting...")

class ElectricCar(Vehicle):
    def __init__(self, brand, model, battery_capacity):
        # Call the parent constructor using super() to initialize brand & model
        super().__init__(brand, model)
        self.battery_capacity = battery_capacity  # New child-specific attribute

    # Overriding method from Vehicle
    def start_engine(self):
        super().start_engine()  # Run parent engine setup first
        print(f"Silent electric power engaged ({self.battery_capacity} kWh battery ready)!")

tesla = ElectricCar("Tesla", "Model 3", 75)
tesla.start_engine()
# Output:
# The engine of Tesla Model 3 is starting...
# Silent electric power engaged (75 kWh battery ready)!

5. Method Resolution Order (MRO)

When using Multiple Inheritance (a class inheriting from two or more parents), Python needs a clear rule to decide which parent method to execute first if both parents share the same method name. This rule is called the Method Resolution Order (MRO), calculated using the C3 Linearization algorithm.

You can inspect the exact method lookup order of any class using ClassName.mro() or ClassName.__mro__.

Python code example:

class Camera:
    def click_photo(self):
        print("Photo taken!")

class Phone:
    def make_call(self):
        print("Calling...")

# Smartphone inherits from both Camera and Phone
class Smartphone(Camera, Phone):
    pass

sp = Smartphone()
sp.click_photo()
sp.make_call()

# Inspecting Method Resolution Order (MRO)
print(Smartphone.mro())
# Output: [<class '__main__.Smartphone'>, <class '__main__.Camera'>, <class '__main__.Phone'>, <class 'object'>]

6. Abstract Base Classes (ABCs)

Sometimes you want to create a parent class that serves strictly as a blueprint and should never be instantiated directly. An Abstract Base Class (ABC) forces child classes to implement specific methods using the @abstractmethod decorator from Python's built-in abc module.

from abc import ABC, abstractmethod

# Abstract Base Class
class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount):
        """Must be implemented by all concrete subclasses!"""
        pass

class CreditCardProcessor(PaymentProcessor):
    def process_payment(self, amount):
        print(f"Processing ${amount} via Credit Card.")

# Attempting to instantiate the Abstract class directly raises an error!
# error_obj = PaymentProcessor() # TypeError: Can't instantiate abstract class

card = CreditCardProcessor()
card.process_payment(100)  # Output: Processing $100 via Credit Card.

7. Common Beginner Mistakes

⚠️ Watch out for these mistakes:

  • Forgetting to call super().__init__(): If you define an __init__ method in a child class, it completely overrides the parent's constructor. If you don't invoke super().__init__(), parent attributes won't be initialized, causing AttributeError later!
  • Deep Inheritance Hierarchies: Creating complex chains of 5+ nested inherited classes makes code fragile and unmaintainable. Favor composition ("has-a" relationships) over inheritance ("is-a" relationships) when possible.
  • The Diamond Problem in Multiple Inheritance: Inheriting from multiple classes that share a common ancestor can cause unexpected method execution orders. Always check ClassName.mro() if behavior seems strange!
  • Using Type Checks Instead of isinstance() / issubclass(): Avoid using type(obj) == ParentClass because it ignores child class inheritance! Always use isinstance(obj, ParentClass) or issubclass(Child, Parent).

8. Code Examples

Here are practical code patterns demonstrating inheritance verification and hierarchical employee structures:

# 1. Type Verification with isinstance() and issubclass()
class Employee:
    pass

class Manager(Employee):
    pass

m = Manager()

print(isinstance(m, Manager))   # Output: True
print(isinstance(m, Employee))  # Output: True (Manager is an Employee!)
print(issubclass(Manager, Employee)) # Output: True

# 2. Complete Multilevel Inheritance Example
class Person:
    def __init__(self, name):
        self.name = name

class Staff(Person):
    def __init__(self, name, staff_id):
        super().__init__(name)
        self.staff_id = staff_id

class Developer(Staff):
    def __init__(self, name, staff_id, tech_stack):
        super().__init__(name, staff_id)
        self.tech_stack = tech_stack

dev = Developer("Alex", "E104", ["Python", "Django", "PostgreSQL"])
print(f"Dev: {dev.name} | ID: {dev.staff_id} | Stack: {', '.join(dev.tech_stack)}")
# Output: Dev: Alex | ID: E104 | Stack: Python, Django, PostgreSQL
Interactive Practice

Mini Practice: Inheritance & super()

Define a parent class Shape with an attribute color. Then create a subclass Circle that inherits from Shape, calls super().__init__() to assign color, and adds its own radius attribute.

0% complete
Check when done
Quiz

Test Your Understanding

1. What is the primary purpose of the `super()` function in a Python child class?

2. Which built-in function checks whether a class is a subclass of another class in Python?

3. What does MRO stand for in Python's multiple inheritance system?

4. Which decorator enforces that concrete child classes MUST implement a method from an Abstract Base Class (ABC)?

9. Challenge Project

Employee Management System: Create a mini hierarchy to represent workplace staff:

  1. Create a base class Employee with attributes name and salary, and a method calculate_bonus() returning 10% of salary.
  2. Create a subclass Manager that inherits from Employee, adds a department attribute using super(), and overrides calculate_bonus() to return 20% of salary + a fixed $5,000 bonus.
  3. Create a subclass Developer that adds a programming_language attribute and overrides calculate_bonus() to add a $2,000 tech bonus.
  4. Instantiate a Manager and Developer, call their bonus methods, and print out their details!

10. Key Takeaways

  • Inheritance lets child classes reuse and extend attributes and methods from parent classes.
  • Use super().__init__() inside child constructors to properly initialize base attributes.
  • Method Overriding allows subclasses to customize or replace parent method implementations.
  • Python supports Single, Multilevel, Hierarchical, and Multiple Inheritance.
  • Use ClassName.mro() to inspect the resolution order Python uses for method lookups.
  • Use Abstract Base Classes (ABCs) to enforce mandatory method contracts on child classes.

🚀 Progression

You've mastered Inheritance & Method Resolution Order! Next up, we will explore **Polymorphism & Encapsulation in Python**!