All Courses
Object-Oriented Programming

Python Inheritance

What is Inheritance?

Inheritance allows one class to reuse the functionality of another class. Instead of duplicating code across related classes, shared logic lives in one place — making it easier to maintain and modify.

class Employee(Person):   # Employee inherits from Person
    pass

Terminology

Term Meaning
Child / Subclass / Derived class The class that inherits (e.g. Employee)
Parent / Superclass / Base class The class being inherited from (e.g. Person)

The direction is one-way: the child gets access to the parent, but not vice versa.


Basic Example

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def say_hello(self):
        print(f"Hi, my name is {self.first_name} {self.last_name}")


class Employee(Person):
    pass   # inherits everything from Person
e = Employee("Tim", "Programmer")
e.say_hello()   # Hi, my name is Tim Programmer  ✅

No __init__ needed in Employee — it uses Person's automatically.


Overriding Methods

Define a method with the same name in the child class to replace the parent's version:

class Employee(Person):
    def say_hello(self):
        print("---")
        super().say_hello()   # calls Person's say_hello
        print("---")
  • super() gives access to the parent class
  • super().say_hello() calls the parent's method on the current instance
  • Without super(), the parent's version is completely replaced

Overriding __init__ (the Constructor)

When adding new attributes in a child class, override __init__ and call the parent's constructor with super():

class Employee(Person):
    def __init__(self, first_name, last_name, salary):
        super().__init__(first_name, last_name)   # must call this!
        self.salary = salary

⚠️ Always call super().__init__() when overriding a constructor. The parent's __init__ may set up attributes that its own methods depend on. Skipping it can cause errors later.


Inheritance Hierarchies

Classes can inherit across multiple levels:

class Person:
    ...

class Employee(Person):    # Employee is a Person
    ...

class Manager(Employee):   # Manager is an Employee AND a Person
    def __init__(self, first_name, last_name, salary, department):
        super().__init__(first_name, last_name, salary)
        self.department = department

When super() is called in Manager, it refers to Employee — not Person directly.


The isinstance() Function

Checks if an object is an instance of a class (including parent classes):

m = Manager("Tim", "Programmer", 50000, "Sports")

isinstance(m, Manager)    # True
isinstance(m, Employee)   # True  — Manager inherits from Employee
isinstance(m, Person)     # True  — Employee inherits from Person
isinstance(m, Owner)      # False — no relation

The "Is-A" Rule

If a class inherits from another, every instance of the child must be an example of the parent.

class Manager(Employee):   # ✅ Valid — every manager IS an employee
class Manager(Owner):      # ❌ Invalid — not every manager IS an owner

Use this rule to decide whether inheritance is appropriate.


Polymorphism

Polymorphism (Greek: "many forms") means an object can be treated as different types depending on context.

  • A Manager instance can be used as a Manager, an Employee, or a Person
  • All share the same interface (e.g. say_hello) but may behave differently if the method is overridden

Multiple Inheritance

Python allows a class to inherit from more than one parent:

class C(A, B):
    pass

Method Resolution Order (MRO)

When a method is called, Python searches in this order: 1. The class itself (C) 2. First listed parent (A) 3. Second listed parent (B)

class A:
    def __init__(self): print("A")

class B:
    def __init__(self): print("B")

class C(A, B):
    pass

C()   # prints "A" — A is checked first

super() also follows MRO — it starts with the first parent and moves to the next only if the method isn't found.

⚠️ Multiple inheritance gets complex quickly. The is-a rule still applies to both parents. Use it sparingly.


Duck Typing

"If it walks like a duck and quacks like a duck, it must be a duck."

Python doesn't check whether an object has a method before running the code. It just tries it:

animals = [Duck(), Duck(), Whale()]

for animal in animals:
    animal.swim()
    animal.fly()   # crashes only when Whale is reached — not before
  • In languages like Java, the code wouldn't even compile — the type mismatch is caught early
  • In Python, it runs until it fails at runtime
  • This is duck typing: Python cares whether an object can do something, not what type it is

Key Takeaways & Recap

Concept Summary
Inheritance Child class reuses parent class functionality
super() Access the parent class; always call in overridden __init__
Method override Redefine a method in the child to change its behaviour
isinstance() Checks type including inherited types
Is-A rule Only inherit if every child instance truly "is a" parent
Polymorphism One object, multiple usable forms depending on context
MRO Left-to-right search order for methods in multiple inheritance
Duck typing Python tries methods at runtime regardless of type