Inheritance in Python

Introduction

Inheritance is a fundamental concept in Object-Oriented Programming that allows a class to inherit properties and methods from another class. This promotes code reusability and establishes relationships between classes.

Note

Inheritance enables a new class (child/derived class) to acquire attributes and methods from an existing class (parent/base class).


Why Inheritance?

Benefits:

  • Code Reusability: Avoid duplicating code by inheriting from existing classes

  • Extensibility: Add new features to existing classes without modifying them

  • Hierarchy: Create logical relationships between classes

  • Maintainability: Changes in parent class automatically reflect in child classes

# Without inheritance - code duplication
class Teacher:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# With inheritance - code reuse
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Teacher(Person):
    pass  # Inherits name and age from Person

class Student(Person):
    pass  # Inherits name and age from Person

Basic Inheritance Syntax

# Parent/Base/Super class
class ParentClass:
    # Parent class members
    pass

# Child/Derived/Sub class
class ChildClass(ParentClass):
    # Child class members
    # Inherits all members from ParentClass
    pass

Simple Inheritance Example

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

    def speak(self):
        return f"{self.name} makes a sound"

    def move(self):
        return f"{self.name} is moving"

# Child class inheriting from Animal
class Dog(Animal):
    def bark(self):
        return f"{self.name} says Woof!"

# Creating objects
animal = Animal("Generic Animal")
print(animal.speak())   # Generic Animal makes a sound
print(animal.move())    # Generic Animal is moving

dog = Dog("Buddy")
print(dog.speak())      # Buddy makes a sound (inherited)
print(dog.move())       # Buddy is moving (inherited)
print(dog.bark())       # Buddy says Woof! (own method)

Adding Constructor in Child Class

When a child class has its own constructor, use super() to call the parent’s constructor.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        return f"Name: {self.name}, Age: {self.age}"

class Student(Person):
    def __init__(self, name, age, roll_number):
        super().__init__(name, age)  # Call parent constructor
        self.roll_number = roll_number

    def display(self):
        return f"{super().display()}, Roll No: {self.roll_number}"

student = Student("Alice", 20, "MCA001")
print(student.display())  # Name: Alice, Age: 20, Roll No: MCA001

Note

super() is used to access methods from the parent class. It’s essential when overriding methods or constructors.


Method Overriding

Child classes can override (redefine) methods from the parent class.

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

    def start(self):
        return f"{self.brand} vehicle is starting"

    def stop(self):
        return f"{self.brand} vehicle is stopping"

class Car(Vehicle):
    def start(self):  # Overriding parent method
        return f"{self.brand} car engine is starting with ignition"

    def honk(self):
        return f"{self.brand} car: Beep Beep!"

class Bicycle(Vehicle):
    def start(self):  # Overriding parent method
        return f"{self.brand} bicycle: Start pedaling!"

    def ring_bell(self):
        return f"{self.brand} bicycle: Ring Ring!"

car = Car("Toyota")
bicycle = Bicycle("Hero")

print(car.start())       # Toyota car engine is starting with ignition (overridden)
print(car.stop())        # Toyota car is stopping (inherited)
print(car.honk())        # Toyota car: Beep Beep!

print(bicycle.start())   # Hero bicycle: Start pedaling! (overridden)
print(bicycle.stop())    # Hero bicycle is stopping (inherited)
print(bicycle.ring_bell())  # Hero bicycle: Ring Ring!

Types of Inheritance

1. Single Inheritance

One child class inherits from one parent class.

class Parent:
    def parent_method(self):
        return "This is parent method"

class Child(Parent):
    def child_method(self):
        return "This is child method"

obj = Child()
print(obj.parent_method())  # Inherited
print(obj.child_method())   # Own method

2. Multiple Inheritance

One child class inherits from multiple parent classes.

class Father:
    def father_trait(self):
        return "Father's trait"

class Mother:
    def mother_trait(self):
        return "Mother's trait"

class Child(Father, Mother):
    def child_trait(self):
        return "Child's trait"

child = Child()
print(child.father_trait())  # Father's trait
print(child.mother_trait())  # Mother's trait
print(child.child_trait())   # Child's trait

Note

In multiple inheritance, if both parents have the same method name, Python uses Method Resolution Order (MRO) to determine which method to call.

3. Multilevel Inheritance

A class inherits from a child class, creating a chain.

class GrandParent:
    def grand_parent_method(self):
        return "GrandParent method"

class Parent(GrandParent):
    def parent_method(self):
        return "Parent method"

class Child(Parent):
    def child_method(self):
        return "Child method"

child = Child()
print(child.grand_parent_method())  # GrandParent method
print(child.parent_method())        # Parent method
print(child.child_method())         # Child method

4. Hierarchical Inheritance

Multiple child classes inherit from one parent class.

class Animal:
    def eat(self):
        return "Eating..."

class Dog(Animal):
    def bark(self):
        return "Woof!"

class Cat(Animal):
    def meow(self):
        return "Meow!"

dog = Dog()
cat = Cat()

print(dog.eat())   # Eating... (inherited)
print(dog.bark())  # Woof!

print(cat.eat())   # Eating... (inherited)
print(cat.meow())  # Meow!

Real-World Example: Employee Management

class Employee:
    def __init__(self, name, emp_id):
        self.name = name
        self.emp_id = emp_id

    def display_info(self):
        return f"Employee: {self.name} (ID: {self.emp_id})"

class Manager(Employee):
    def __init__(self, name, emp_id, department):
        super().__init__(name, emp_id)
        self.department = department
        self.team = []

    def add_team_member(self, member):
        self.team.append(member)

    def display_info(self):
        info = super().display_info()
        return f"{info}\nRole: Manager\nDepartment: {self.department}\nTeam Size: {len(self.team)}"

class Developer(Employee):
    def __init__(self, name, emp_id, programming_language):
        super().__init__(name, emp_id)
        self.programming_language = programming_language

    def display_info(self):
        info = super().display_info()
        return f"{info}\nRole: Developer\nLanguage: {self.programming_language}"

# Creating objects
manager = Manager("John Smith", "M001", "IT")
dev1 = Developer("Alice Johnson", "D001", "Python")
dev2 = Developer("Bob Williams", "D002", "Java")

manager.add_team_member(dev1.name)
manager.add_team_member(dev2.name)

print(manager.display_info())
print("\n" + dev1.display_info())
print("\n" + dev2.display_info())

Complete Example: Banking System

class BankAccount:
    def __init__(self, account_number, holder_name, balance=0):
        self.account_number = account_number
        self.holder_name = holder_name
        self.balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
            return f"Deposited ₹{amount}. Balance: ₹{self.balance}"
        return "Invalid amount"

    def withdraw(self, amount):
        if amount > 0 and amount <= self.balance:
            self.balance -= amount
            return f"Withdrew ₹{amount}. Balance: ₹{self.balance}"
        return "Insufficient funds or invalid amount"

    def get_balance(self):
        return f"Balance: ₹{self.balance}"

class SavingsAccount(BankAccount):
    def __init__(self, account_number, holder_name, balance=0, interest_rate=4.0):
        super().__init__(account_number, holder_name, balance)
        self.interest_rate = interest_rate

    def add_interest(self):
        interest = self.balance * self.interest_rate / 100
        self.balance += interest
        return f"Interest of ₹{interest:.2f} added at {self.interest_rate}%"

class CurrentAccount(BankAccount):
    def __init__(self, account_number, holder_name, balance=0, overdraft_limit=10000):
        super().__init__(account_number, holder_name, balance)
        self.overdraft_limit = overdraft_limit

    def withdraw(self, amount):
        if amount > 0 and amount <= (self.balance + self.overdraft_limit):
            self.balance -= amount
            return f"Withdrew ₹{amount}. Balance: ₹{self.balance}"
        return "Overdraft limit exceeded"

# Using the classes
savings = SavingsAccount("SA001", "Alice", 10000)
print(savings.deposit(5000))       # Deposited ₹5000. Balance: ₹15000
print(savings.add_interest())      # Interest of ₹600.00 added at 4.0%
print(savings.get_balance())       # Balance: ₹15600.0

current = CurrentAccount("CA001", "Bob", 5000)
print(current.withdraw(7000))      # Withdrew ₹7000. Balance: ₹-2000
print(current.withdraw(9000))      # Withdrew ₹9000. Balance: ₹-11000
print(current.withdraw(1000))      # Overdraft limit exceeded

Using isinstance() and issubclass()

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
cat = Cat()

# isinstance() checks if object is instance of a class
print(isinstance(dog, Dog))      # True
print(isinstance(dog, Animal))   # True (Dog is subclass of Animal)
print(isinstance(dog, Cat))      # False

# issubclass() checks if a class is subclass of another
print(issubclass(Dog, Animal))   # True
print(issubclass(Cat, Animal))   # True
print(issubclass(Dog, Cat))      # False

Multiple Inheritance Example

class Teacher:
    def __init__(self, subject):
        self.subject = subject

    def teach(self):
        return f"Teaching {self.subject}"

class Researcher:
    def __init__(self, research_area):
        self.research_area = research_area

    def research(self):
        return f"Researching {self.research_area}"

class Professor(Teacher, Researcher):
    def __init__(self, name, subject, research_area):
        self.name = name
        Teacher.__init__(self, subject)
        Researcher.__init__(self, research_area)

    def display(self):
        return f"Professor {self.name}\n{self.teach()}\n{self.research()}"

prof = Professor("Dr. Smith", "Python Programming", "Machine Learning")
print(prof.display())
# Output:
# Professor Dr. Smith
# Teaching Python Programming
# Researching Machine Learning

Tasks

Task 1: Shape Hierarchy

Create a base class Shape with method area(). Create child classes Rectangle and Circle that inherit from Shape and override the area() method with proper calculations.

Hint: Rectangle area = length × width, Circle area = π × radius². Use 3.14159 for π.

Task 2: Vehicle Rental System

Create a Vehicle base class with attributes brand and rental_price. Create Car and Bike child classes with additional attributes (num_doors for Car, type for Bike). Override a method to display rental information.

Hint: Use super().__init__() in child constructors. Add specific attributes after calling parent constructor.

Task 3: Student and Teacher

Create a Person base class with name and age. Create Student (with roll_no and marks) and Teacher (with subject and salary) child classes. Add methods to display information for each.

Hint: Use super().display() in child classes to include parent information.

Task 4: Multiple Inheritance - Smart Device

Create classes Phone (with call method) and Camera (with take_photo method). Create Smartphone class that inherits from both and adds internet_browsing method.

Hint: Use class Smartphone(Phone, Camera): for multiple inheritance. Call both parent constructors.

Task 5: Multilevel Inheritance - Organization

Create Organization base class, Department child class, and Team grandchild class. Each level should add its own attributes and methods. Demonstrate accessing methods from all levels.

Hint: Use multilevel inheritance: Team(Department) and Department(Organization). Use super() to access parent methods.


Summary

  • Inheritance allows code reuse by inheriting from existing classes

  • Use class ChildClass(ParentClass): syntax

  • super() accesses parent class methods and constructors

  • Method overriding allows child classes to provide specific implementations

  • Types: Single, Multiple, Multilevel, Hierarchical inheritance

  • isinstance() checks object type, issubclass() checks class relationships

  • Inheritance creates “is-a” relationships (Dog is a Animal)