DEV Community

Thiruvengadam Sakthivel
Thiruvengadam Sakthivel

Posted on

#03 – Inheritance & Polymorphism: The Backbone of Python OOP

Welcome to Day 3! Today we explore how classes share, extend, and adapt behavior without duplicating code.

  1. Inheritance: Creating new classes based on existing ones to reuse code and establish hierarchies.
  2. Method Overriding & super(): Redefining parent class behaviors in child classes while retaining access to parent implementations.
  3. Polymorphism & Duck Typing: Operating on different object types through a shared interface or common set of methods.

1. Inheritance Structures 🌳

Python supports three primary flavors of inheritance:

  SINGLE INHERITANCE         MULTILEVEL INHERITANCE        MULTIPLE INHERITANCE

     ┌──────────┐                 ┌──────────┐           ┌───────┐   ┌─────────┐
     │  Parent  │                 │  Grandpa │           │ Mixin │   │ BaseClass│
     └────┬─────┘                 └────┬─────┘           └───┬───┘   └───┬─────┘
          │                            │                     │           │
          ▼                            ▼                     └─────┬─────┘
     ┌──────────┐                 ┌──────────┐                     ▼
     │  Child   │                 │  Parent  │               ┌───────────┐
     └──────────┘                 └────┬─────┘               │   Child   │
                                       │                     └───────────┘
                                       ▼
                                  ┌──────────┐
                                  │  Child   │
                                  └──────────┘

Enter fullscreen mode Exit fullscreen mode
  • Single Inheritance: One child class inherits from one direct parent class.
  • Multilevel Inheritance: A child class inherits from a parent class, which itself inherits from a grandparent class (a chain).
  • Multiple Inheritance: A single child class inherits directly from two or more parent classes.

2. Method Overriding & super() 🔄

  • Method Overriding: Occurs when a child class defines a method with the exact same name as a method in its parent class. The child's method replaces or modifies the parent behavior.
  • super(): A built-in function that lets you invoke methods from a parent class inside the child class. This prevents code repetition when extending parent functionality.

🌱 Quick Example

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

    def make_sound(self) -> str:
        return "Generic animal sound"

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        # Call parent constructor using super()
        super().__init__(name)
        self.breed = breed

    # Override parent method
    def make_sound(self) -> str:
        parent_sound = super().make_sound()
        return f"{parent_sound} -> Woof! 🐾"

dog = Dog("Rex", "German Shepherd")
print(dog.make_sound())  # Output: Generic animal sound -> Woof! 🐾

Enter fullscreen mode Exit fullscreen mode

3. Polymorphism & Duck Typing 🦆

Polymorphism ("many shapes") allows different classes to respond to the same method call in their own unique way.

In Python, polymorphism is heavily tied to Duck Typing:

"If it walks like a duck and quacks like a duck, it's a duck."

Python doesn't care about explicit class hierarchies or explicit interface contracts—if an object implements the expected method (e.g., .calculate_pay()), Python will happily execute it.

                     ┌───────────────────────────────┐
                     │   Payroll Processor Service   │
                     └───────────────┬───────────────┘
                                     │
                      Calls .calculate_pay() on each
                                     │
      ┌──────────────────────────────┼──────────────────────────────┐
      ▼                              ▼                              ▼
┌──────────┐                   ┌──────────┐                   ┌──────────┐
│ FullTime │                   │ Executive│                   │Contractor│
│ Employee │                   │ Manager  │                   │ (No Base)│
└──────────┘                   └──────────┘                   └──────────┘

Enter fullscreen mode Exit fullscreen mode

4. Practice Challenge: Building an Employee Management System 🏢

Here is a enterprise-ready Employee Management System demonstrating Single, Multilevel, and Multiple Inheritance, Method Overriding with super(), and Duck Typing via a unified PayrollProcessor.

Step 1: Initialize Your Workspace

uv init oop_day3 && cd oop_day3
touch employee_system.py

Enter fullscreen mode Exit fullscreen mode

Step 2: Implement employee_system.py

# employee_system.py
from typing import List, Any


# ==========================================
# 1. BASE CLASS (PARENT)
# ==========================================
class Employee:
    """Base class for all salaried employees."""

    def __init__(self, emp_id: str, name: str, base_salary: float):
        self.emp_id = emp_id
        self.name = name
        self.base_salary = base_salary

    def calculate_pay(self) -> float:
        """Calculates standard monthly pay."""
        return round(self.base_salary, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Employee | Base Salary: ${self.base_salary:,.2f}"


# ==========================================
# 2. SINGLE INHERITANCE: DEVELOPER
# ==========================================
class Developer(Employee):
    """Inherits directly from Employee (Single Inheritance)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, tech_stack: str):
        super().__init__(emp_id, name, base_salary)
        self.tech_stack = tech_stack

    # Override calculate_pay to add a tech allowance
    def calculate_pay(self) -> float:
        tech_bonus = 300.00  # Fixed monthly tooling stipend
        return round(super().calculate_pay() + tech_bonus, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Developer ({self.tech_stack})"


# ==========================================
# 3. MULTILEVEL INHERITANCE: MANAGER -> EXECUTIVE
# ==========================================
class Manager(Employee):
    """Inherits from Employee (Level 1)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, team_size: int):
        super().__init__(emp_id, name, base_salary)
        self.team_size = team_size

    def calculate_pay(self) -> float:
        team_allowance = self.team_size * 150.00
        return round(super().calculate_pay() + team_allowance, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Manager | Team Size: {self.team_size}"


class Executive(Manager):
    """Inherits from Manager (Level 2 - Multilevel Inheritance: Employee -> Manager -> Executive)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, team_size: int, stock_options: int):
        super().__init__(emp_id, name, base_salary, team_size)
        self.stock_options = stock_options

    def calculate_pay(self) -> float:
        executive_bonus = 2500.00
        # Combines base salary + team allowance (from Manager) + executive bonus
        return round(super().calculate_pay() + executive_bonus, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Executive | Options: {self.stock_options} units"


# ==========================================
# 4. MULTIPLE INHERITANCE / MIXIN: AUDITABLE
# ==========================================
class ComplianceAuditableMixin:
    """A standalone mixin class that adds auditing capabilities."""

    def log_audit(self, action: str) -> None:
        print(f"[AUDIT LOG] Entity Action Recorded: '{action}'")


class TechLead(Developer, ComplianceAuditableMixin):
    """Inherits from Developer AND ComplianceAuditableMixin (Multiple Inheritance)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, tech_stack: str, projects_count: int):
        Developer.__init__(self, emp_id, name, base_salary, tech_stack)
        self.projects_count = projects_count

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Tech Lead | Projects: {self.projects_count}"


# ==========================================
# 5. DUCK TYPING: CONTRACTOR (NO BASE CLASS)
# ==========================================
class ExternalContractor:
    """Does NOT inherit from Employee, but implements calculate_pay() and get_details()."""

    def __init__(self, contractor_id: str, name: str, hourly_rate: float, hours_worked: float):
        self.contractor_id = contractor_id
        self.name = name
        self.hourly_rate = hourly_rate
        self.hours_worked = hours_worked

    def calculate_pay(self) -> float:
        return round(self.hourly_rate * self.hours_worked, 2)

    def get_details(self) -> str:
        return f"[{self.contractor_id}] {self.name} | External Contractor ({self.hours_worked} hrs @ ${self.hourly_rate}/hr)"


# ==========================================
# 6. UNIFIED PAYROLL PROCESSOR (POLYMORPHISM)
# ==========================================
class PayrollProcessor:
    """Uses Polymorphism & Duck Typing to process pay across any compatible worker object."""

    @staticmethod
    def process_payroll(workers: List[Any]) -> None:
        print("==========================================================")
        print("             MONTHLY PAYROLL REPORT                       ")
        print("==========================================================")
        total_payout = 0.0

        for worker in workers:
            # Duck typing in action: as long as worker has get_details() and calculate_pay(), it works
            details = worker.get_details()
            pay = worker.calculate_pay()
            total_payout += pay
            print(f"{details}\n  └─ Calculated Payout: ${pay:,.2f}\n")

        print("----------------------------------------------------------")
        print(f" TOTAL SYSTEM PAYOUT: ${total_payout:,.2f}")
        print("==========================================================\n")


# ==========================================
# TEST EXECUTION SUITE
# ==========================================
if __name__ == "__main__":
    # Create instances across various inheritance patterns
    dev = Developer("EMP-101", "Alice Chen", base_salary=7500.00, tech_stack="Python/FastAPI")
    mgr = Manager("EMP-102", "Bob Smith", base_salary=9000.00, team_size=5)
    exec_officer = Executive("EMP-103", "Carol Danvers", base_salary=15000.00, team_size=20, stock_options=5000)

    # Multiple Inheritance instance
    tech_lead = TechLead("EMP-104", "David Miller", base_salary=11000.00, tech_stack="Distributed Systems", projects_count=3)
    tech_lead.log_audit("Approved Architecture Review")  # Mixin functionality

    # Duck Typing instance (No inheritance from Employee class)
    contractor = ExternalContractor("CONT-801", "Eva Green", hourly_rate=85.00, hours_worked=120)

    # Heterogeneous collection passed into PayrollProcessor
    workforce = [dev, mgr, exec_officer, tech_lead, contractor]

    # Run Polymorphic Payroll
    PayrollProcessor.process_payroll(workforce)

Enter fullscreen mode Exit fullscreen mode

Step 3: Run & Verify Execution

uv run employee_system.py

Enter fullscreen mode Exit fullscreen mode

Output Summary

[AUDIT LOG] Entity Action Recorded: 'Approved Architecture Review'

==========================================================
             MONTHLY PAYROLL REPORT                       
==========================================================
[EMP-101] Alice Chen | Role: Developer (Python/FastAPI)
  └─ Calculated Payout: $7,800.00

[EMP-102] Bob Smith | Role: Manager | Team Size: 5
  └─ Calculated Payout: $9,750.00

[EMP-103] Carol Danvers | Role: Executive | Options: 5000 units
  └─ Calculated Payout: $20,500.00

[EMP-104] David Miller | Role: Tech Lead | Projects: 3
  └─ Calculated Payout: $11,300.00

[CONT-801] Eva Green | External Contractor (120 hrs @ $85.0/hr)
  └─ Calculated Payout: $10,200.00

----------------------------------------------------------
 TOTAL SYSTEM PAYOUT: $59,550.00
==========================================================

Enter fullscreen mode Exit fullscreen mode

Top comments (0)