←Back to Subject
Notes
Notes
Study material and explanations
Diagrams
Diagrams
Visualize concepts easily
Mnemonics
Mnemonics
Tricks to remember faster
One Night Prep
One Night Prep
Short and focused revision
Revision
Revision
Quick summary and key points
Important Questions
Important Questions
Practice most relevant questions
Notes

Inheritance, Polymorphism & Inner Classes in Python

Study material and explanations for Unit 4

Inheritance, Polymorphism & Inner Classes in Python

In this note, we explore the remaining two core pillars of Object-Oriented Programming (Inheritance and Polymorphism) along with Inner (Nested) Classes.


1. Principle 3: Inheritance in Python

Inheritance is the mechanism by which a new class (Child / Derived / Subclass) acquires all the attributes and methods of an existing class (Parent / Base / Superclass).

  • Main Benefit: Code Reusability — write common code once in the Parent class and reuse it across multiple Child classes!

Syntax of Inheritance in Python:

Instead of keywords like extends (in Java), Python passes the Parent class inside parentheses ( ):

class Parent:
    # parent methods

class Child(Parent):
    # child inherits everything from Parent

Types of Inheritance in Python

Unlike Java, Python directly supports Multiple Inheritance using classes!

5 Types of Inheritance Supported in Python
1. Single
Parent A
↓
Child B
2. Multilevel
Grandparent A
↓
Parent B → Child C
3. Multiple
Father A   Mother B
↘      ↙
Child C
4. Hierarchical
Parent A
↙      ↘
Child B   Child C
5. Hybrid
Combination of two or more types of inheritance

Example — Inheritance and the super() Function:

The built-in super() function is used inside a Child class to call the constructor (__init__) or methods of its Parent class.

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

    def show_name(self):
        print("Name:", self.name)

# Child Class inheriting from Person
class Employee(Person):
    def __init__(self, name, emp_id, salary):
        super().__init__(name)   # Calls Parent class constructor
        self.emp_id = emp_id
        self.salary = salary

    def show_details(self):
        self.show_name()         # Reusing Parent method
        print(f"ID: {self.emp_id} | Salary: Rs.{self.salary}")

emp = Employee("Vikram", "EMP105", 45000)
emp.show_details()

Example — Multiple Inheritance (class Child(Father, Mother)):

class Father:
    def skill_driving(self):
        print("Knows Car Driving")

class Mother:
    def skill_painting(self):
        print("Knows Oil Painting")

# Child inherits from BOTH Father and Mother
class Child(Father, Mother):
    pass

c = Child()
c.skill_driving()
c.skill_painting()

2. Principle 4: Polymorphism in Python

The word Polymorphism comes from Greek (Poly = Many, Morphs = Forms). It means the same function name or operator behaving differently depending on the object or data type.

Ways Polymorphism Works in Python:

A. Built-in Polymorphism (Operator & Function Overloading)

  • The + operator performs arithmetic addition for numbers (10 + 20 = 30), but performs concatenation for strings ("Hello " + "World").
  • The len() function counts characters in a string (len("BCA") = 3), items in a list (len([10, 20]) = 2), or keys in a dictionary.

B. Method Overriding (Runtime Polymorphism)

When a Child class provides its own specific implementation of a method that is already defined in its Parent class, it is called Method Overriding.

class Animal:
    def sound(self):
        print("Animal makes a sound")

class Dog(Animal):
    # Overriding parent's sound() method
    def sound(self):
        print("Dog Barks: Woof Woof!")

class Cat(Animal):
    # Overriding parent's sound() method
    def sound(self):
        print("Cat Meows: Meow!")

# Polymorphism in action: same method call behaves differently!
for pet in [Dog(), Cat()]:
    pet.sound()

3. Inner Classes (Nested Classes) in Python

An Inner Class (or Nested Class) is a class defined entirely inside the body of another class (the Outer Class).

Why Use Inner Classes?

  1. Logical Grouping: When one class is tightly coupled with another class and has no meaning outside it (for example, a Laptop class having an inner CPU class, or a Student class having an inner Address or DOB class).
  2. Better Encapsulation & Organization: Keeps helper classes hidden inside the main outer class.

Example of Inner Class in Python:

class Student:                          # Outer Class
    def __init__(self, name, roll_no, brand, ram):
        self.name = name
        self.roll_no = roll_no
        # Creating object of Inner Class inside Outer Class constructor
        self.lap = self.Laptop(brand, ram)

    def show_student(self):
        print(f"Student: {self.name} (Roll: {self.roll_no})")
        self.lap.show_laptop()

    class Laptop:                       # Inner Class (inside Student)
        def __init__(self, brand, ram):
            self.brand = brand
            self.ram = ram

        def show_laptop(self):
            print(f"Laptop Specs: {self.brand}, {self.ram}GB RAM")

# Creating Outer Class object automatically initializes Inner Class object
s1 = Student("Karan", 101, "Dell Inspiron", 16)
s1.show_student()

Output:

Student: Karan (Roll: 101)
Laptop Specs: Dell Inspiron, 16GB RAM

(Note: You can also create an Inner Class object directly from outside using lap_obj = Student.Laptop("HP", 8)).