Post

15. Object-Oriented Programming - Inheritance

🚀 Unlock the true potential of code reuse and extensibility! This post guides you through the intricate world of Object-Oriented Inheritance, covering single, multiple, and multilevel types, mastering the `super()` function, and demystifying Method Resolution Order (MRO). Elevate your OOP design skills today! 💡

15. Object-Oriented Programming - Inheritance

What we will learn in this post?

  • 👉 Introduction to Inheritance
  • 👉 Single Inheritance
  • 👉 The super() Function
  • 👉 Multiple Inheritance
  • 👉 Method Resolution Order (MRO)
  • 👉 Multilevel Inheritance
  • 👉 Method Overriding and Extension
  • 👉 Conclusion!

Inheritance: Building Smarter Classes! 🏗️

What is Inheritance? 🧬

Inheritance is like cloning a class but with an upgrade! It’s a powerful way to create a new class based on an existing one. Think of it as passing down traits. The new class, called the child or derived class, automatically gets all the features (methods and attributes) from the original class, known as the parent or base class.

Why Use Inheritance? ✨

It’s fantastic for:

  • Code Reusability: Instead of writing the same code multiple times, you write it once in the parent class. Children can then reuse it effortlessly!
  • IS-A Relationship: It helps model real-world connections. For example, a Car IS-A Vehicle.

Parent & Child Classes Explained 👨‍👩‍👧‍👦

  • Parent/Base Class: The “original” class that provides common features.
  • Child/Derived Class: The “new” class that inherits features from its parent and can also add its own unique ones.

The “IS-A” Connection 🤝

This relationship is key! If you can say “X IS-A Y”, then X can likely inherit from Y.

  • A Dog IS-A Animal.
  • A Sedan IS-A Car. This structure makes your code organized and easy to understand.

Visualizing the “IS-A” Relationship:

graph TD
    ANIMAL["🦤 Parent: Animal"]:::gold --> DOG["🐶 Child: Dog (IS-A Animal)"]:::purple
    ANIMAL --> CAT["🐱 Child: Cat (IS-A Animal)"]:::pink

    classDef gold fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef purple fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef pink fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class ANIMAL gold;
    class DOG purple;
    class CAT pink;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Benefits at a Glance:

  • Saves time by reducing duplicate code.
  • Makes your code more structured and maintainable.

Understanding Single Inheritance: The Family Tree! 🌳

Imagine your code elements forming a family tree! Single inheritance is a fundamental concept where a child class (also known as a subclass) is created from just one parent class (the superclass). The child automatically gets all the parent’s cool features, like its methods and attributes. It’s like inheriting traits from only one parent! This helps you reuse code and build on existing structures.

graph TD
    PARENT["🎯 Parent Class"]:::teal --> CHILD["🌱 Child Class"]:::purple

    classDef teal fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef purple fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class PARENT teal;
    class CHILD purple;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Creating Your Child Class 🧒

To make a child class, you simply put the parent’s name in parentheses after the child’s name in its definition.

1
2
3
4
5
6
7
8
# Our Parent Class
class Animal:
    def speak(self):
        return "Generic animal sound"

# Dog is a Child Class of Animal
class Dog(Animal):
    pass # It inherits the 'speak' method from Animal

Using Parent’s Powers: super()! 💪

Sometimes, a child wants to use its parent’s methods and add its own twist. The super() function helps you call the parent class’s method directly, allowing for flexible extensions.

1
2
3
4
5
6
7
8
9
class Dog(Animal):
    def speak(self):
        # Call the parent's speak method using super()
        parent_sound = super().speak()
        return f"Woof! My sound is {parent_sound}"

my_dog = Dog()
print(my_dog.speak())
# Output: Woof! My sound is Generic animal sound

Making It Your Own: Overriding Methods 🎤

If a child class wants to completely change how a method works compared to its parent, it can override it. Just define a method with the same name in the child class; this new method will be used instead.

1
2
3
4
5
6
7
class Cat(Animal):
    def speak(self): # This method overrides the parent's 'speak'
        return "Meow!"

my_cat = Cat()
print(my_cat.speak())
# Output: Meow!

Practical Examples: Inheritance in Real Applications 💼

Below are practical examples showing how inheritance is used to build scalable, reusable code in real-world scenarios:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# Example 1 — Payment Processing System (E-commerce)
class Payment:
    def __init__(self, amount, currency="USD"):
        self.amount = amount
        self.currency = currency
        self.status = "pending"
    
    def process(self):
        return "Processing generic payment"

class CreditCardPayment(Payment):
    def __init__(self, amount, card_number, cvv):
        super().__init__(amount)  # Initialize parent
        self.card_number = card_number[-4:]  # Store last 4 digits only
        self.cvv = cvv
    
    def process(self):
        # Override with credit card specific logic
        return f"Processing credit card payment of ${self.amount} (Card: ****{self.card_number})"

class PayPalPayment(Payment):
    def __init__(self, amount, email):
        super().__init__(amount)
        self.email = email
    
    def process(self):
        return f"Processing PayPal payment of ${self.amount} via {self.email}"

cc_payment = CreditCardPayment(99.99, "1234567890123456", "123")
paypal_payment = PayPalPayment(149.99, "user@example.com")
print(cc_payment.process())  # Processing credit card payment of $99.99 (Card: ****3456)
print(paypal_payment.process())  # Processing PayPal payment of $149.99 via user@example.com


# Example 2 — Employee Management System (HR Applications)
class Employee:
    def __init__(self, name, employee_id, base_salary):
        self.name = name
        self.employee_id = employee_id
        self.base_salary = base_salary
    
    def calculate_salary(self):
        return self.base_salary
    
    def get_details(self):
        return f"{self.name} (ID: {self.employee_id})"

class Manager(Employee):
    def __init__(self, name, employee_id, base_salary, team_size):
        super().__init__(name, employee_id, base_salary)
        self.team_size = team_size
    
    def calculate_salary(self):
        # Managers get bonus based on team size
        bonus = self.team_size * 1000
        return self.base_salary + bonus

class Developer(Employee):
    def __init__(self, name, employee_id, base_salary, programming_languages):
        super().__init__(name, employee_id, base_salary)
        self.programming_languages = programming_languages
    
    def calculate_salary(self):
        # Developers get bonus for multiple languages
        language_bonus = len(self.programming_languages) * 500
        return self.base_salary + language_bonus

manager = Manager("Alice", "M001", 80000, 5)
developer = Developer("Bob", "D001", 70000, ["Python", "JavaScript", "Go"])
print(f"{manager.get_details()} - Salary: ${manager.calculate_salary()}")  # $85,000
print(f"{developer.get_details()} - Salary: ${developer.calculate_salary()}")  # $71,500


# Example 3 — Notification System (Multi-channel messaging)
class Notification:
    def __init__(self, message, recipient):
        self.message = message
        self.recipient = recipient
        self.timestamp = None
    
    def send(self):
        return "Sending notification"

class EmailNotification(Notification):
    def __init__(self, message, recipient, subject):
        super().__init__(message, recipient)
        self.subject = subject
    
    def send(self):
        return f"Sending email to {self.recipient}: {self.subject}"

class SMSNotification(Notification):
    def __init__(self, message, recipient, phone_number):
        super().__init__(message, recipient)
        self.phone_number = phone_number
    
    def send(self):
        return f"Sending SMS to {self.phone_number}: {self.message[:50]}"

email = EmailNotification("Your order has shipped!", "customer@example.com", "Order Update")
sms = SMSNotification("Your OTP is 123456", "Customer", "+1234567890")
print(email.send())  # Sending email to customer@example.com: Order Update
print(sms.send())  # Sending SMS to +1234567890: Your OTP is 123456
🚀 Try this Live → Click to open interactive PYTHON playground

🦸‍♀️ Understanding super() in Python

super() is your friendly helper in Python! It’s used to call methods from the parent (or superclass) of your current class. It’s especially crucial in __init__ to ensure the parent class is properly initialized before the child class adds its own specific features. Think of it as making sure the “foundation” is laid first!

💡 Why super() is Awesome (vs. Direct Calls)

Using super() is generally preferred over direct calls like ParentClass.__init__(self, args) because:

  • 🛡️ Robustness: super() automatically finds the correct parent method, even in complex “family trees” (like multiple inheritance), using the Method Resolution Order (MRO). You don’t need to know the parent’s exact name!
  • ✍️ Maintainability: If a parent class name changes, your code using super() remains unaffected. Direct calls would break!
  • ⚙️ Flexibility: It makes your inheritance structure more adaptable and future-proof.

👨‍👩‍👧‍👦 Let’s See an Example

Here’s how a child class properly calls its parent’s __init__ method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal:
    def __init__(self, name):
        print(f"Animal '{name}' is created!")
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 🎉 Calls Animal's __init__ first!
        print(f"Dog '{name}' ({breed}) is ready!")
        self.breed = breed

my_dog = Dog("Buddy", "Golden Retriever")
# Output:
# Animal 'Buddy' is created!
# Dog 'Buddy' (Golden Retriever) is ready!

Understanding Multiple Inheritance in Python 🐍

Multiple inheritance allows a class to combine features from several parent classes. Think of it like a child getting traits from both mom and dad, allowing for powerful code reuse by inheriting methods and attributes from multiple sources.

Syntax: How it Looks ✨

It’s straightforward! You simply list all parent classes you want to inherit from, separated by commas, when defining your new class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Walkable:
    def walk(self):
        print("I can walk!")

class Talkable:
    def talk(self):
        print("I can talk!")

class Human(Walkable, Talkable): # Inherits from both!
    def __init__(self, name):
        self.name = name
    def introduce(self):
        print(f"Hi, I'm {self.name}.")

# Example usage:
# person = Human("Alice")
# person.walk() # From Walkable
# person.talk() # From Talkable

Potential Complexities: The MRO Maze 🤯

The main challenge is the Method Resolution Order (MRO). If multiple parent classes have methods with the same name, Python needs a clear rule to decide which one to call. This is known as the “diamond problem”. Python uses a consistent C3 linearization algorithm for MRO, which you can inspect using Human.__mro__ or help(Human). Using super() correctly is essential in complex hierarchies to ensure all parent methods are called.

Use Cases: When to Use It? ✅

Multiple inheritance is often best used with Mixin classes. Mixins are small, focused classes that provide optional specific functionalities (like Walkable or Talkable above) that can be “mixed in” to other classes. They are typically not meant to be standalone objects.

  • Good for: Adding distinct capabilities (e.g., SerializableMixin, LoggerMixin).
  • Avoid for: Deep, complex “is-a” relationships to prevent MRO issues.

For more detailed info, refer to the official Python documentation on inheritance.

Visualizing Inheritance 🌳

graph TD
    WALK["🚶 Walkable"]:::teal --> HUMAN["🧑 Human"]:::purple
    TALK["🗣️ Talkable"]:::pink --> HUMAN

    classDef teal fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef pink fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef purple fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class WALK teal;
    class TALK pink;
    class HUMAN purple;

    linkStyle default stroke:#e67e22,stroke-width:3px;

🐍 Understanding Python’s MRO

Hey there! Ever wondered how Python knows which method to use when you have classes inheriting from multiple others? That’s where MRO (Method Resolution Order) comes in! It’s Python’s way of defining the search path for methods and attributes in an inheritance hierarchy.

🕵️‍♀️ How Python Resolves Methods

When you call a method on an object, Python doesn’t just pick one randomly. It follows a specific, ordered list of classes to find that method, starting from your object’s class and moving up through its parents. The first class in this list that defines the method is the one whose method gets called.

🧠 The C3 Linearization Algorithm

Python uses the C3 linearization algorithm to calculate this MRO. C3 ensures a consistent and predictable search order, especially vital for multiple inheritance to avoid ambiguities like the “diamond problem”. It guarantees that the order respects local precedence (a class is checked before its parents) and monotonicity (if a class X precedes Y in the MRO of one class, it will precede Y in the MRO of any subclass). For more on C3, check out Python’s documentation.

🔍 The __mro__ Attribute

You can inspect the MRO of any class using the special __mro__ attribute. It returns a tuple of classes in the order Python will search them.

🏡 Multiple Inheritance Example

Let’s see MRO in action with a classic multiple inheritance scenario!

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
    def show_origin(self): return "Hello from A"

class B(A): # B inherits from A
    pass # B doesn't override show_origin

class C(A): # C also inherits from A
    def show_origin(self): return "Greetings from C" # C overrides it

class D(B, C): # D inherits from B, then C
    pass

# Let's visualize the inheritance:
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#ff4f81','primaryTextColor':'#fff','primaryBorderColor':'#c43e3e','lineColor':'#e67e22','secondaryColor':'#6b5bff','tertiaryColor':'#ffd700'}}}%%
classDiagram
    class A {
        +show_origin()
    }
    class B
    class C {
        +show_origin()
    }
    class D

    A <|-- B
    A <|-- C
    B <|-- D
    C <|-- D
    
    style A fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14
    style B fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14
    style C fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14
    style D fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14
1
2
3
4
5
6
7
8
# Check D's Method Resolution Order
print(f"D's MRO: {D.__mro__}")
# Output: D's MRO: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

# Create an instance of D and call the method
d_obj = D()
print(f"Method call result: {d_obj.show_origin()}")
# Output: Method call result: Greetings from C

Explanation: Python searches D’s MRO: D -> B -> C -> A -> object.

  • D doesn’t have show_origin.
  • B doesn’t have show_origin.
  • C does have show_origin, so C’s version is called!

This ensures a clear and predictable way Python handles method calls in complex class hierarchies. Cool, right?

Multilevel Inheritance: The Family Tree! 🌳

Multilevel inheritance is like a family tree where one class inherits from another, which itself inherited from yet another class. Imagine a Grandparent class (e.g., Vehicle), a Parent class (e.g., Car) that inherits from Grandparent, and a Child class (e.g., SportsCar) that inherits from Parent. This creates a clear inheritance chain.

🎁 Passing Down Traits & Skills

Attributes (like characteristics) and methods (like actions) flow down the hierarchy.

  • The Parent class automatically gets all features from its Grandparent.
  • Consequently, the Child class inherits all features from its Parent, and indirectly also from the Grandparent!

This means a Child object can effortlessly use methods or access attributes defined anywhere up its inheritance chain, promoting code reuse and helping build specialized classes. We often use super() to properly initialize parent classes.

🚀 Let’s See It in Action!

Code Example: The Family Car 🚗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Grandparent Class
class Vehicle:
    def __init__(self, speed):
        self.max_speed = speed
    def drive(self):
        return f"Driving the vehicle at {self.max_speed} mph."

# Parent Class (inherits from Vehicle)
class Car(Vehicle):
    def __init__(self, speed, wheels):
        super().__init__(speed) # Call Grandparent's init
        self.num_wheels = wheels
    def honk(self):
        return "Beep beep!"

# Child Class (inherits from Car)
class SportsCar(Car):
    def __init__(self, speed, wheels, boost):
        super().__init__(speed, wheels) # Call Parent's init
        self.turbo_boost = boost

    def activate_boost(self):
        return f"Activating turbo boost of {self.turbo_boost}!"

# Creating an object of the Child class
my_sports_car = SportsCar(200, 4, "50HP")

# Accessing inherited attributes and methods:
print(my_sports_car.max_speed)        # Inherited from Vehicle (Grandparent)
print(my_sports_car.drive())          # Inherited from Vehicle (Grandparent)
print(my_sports_car.num_wheels)       # Inherited from Car (Parent)
print(my_sports_car.honk())           # Inherited from Car (Parent)
print(my_sports_car.activate_boost()) # From SportsCar itself (Child)
graph TD
    VEHICLE["🚗 Vehicle (Grandparent)"]:::gold --> CAR["🚙 Car (Parent)"]:::purple
    CAR --> SPORTS["🏎️ SportsCar (Child)"]:::pink

    classDef gold fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef purple fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;
    classDef pink fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14,shadow:6px;

    class VEHICLE gold;
    class CAR purple;
    class SPORTS pink;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Method Overriding: Changing a Parent’s Tune! 🎶

Method overriding is when a child class (derived class) provides its own specific implementation for a method that is already defined in its parent class (base class). It’s like a child saying, “I’ll do this task, but in my own special way!” This is useful when a generic parent method needs a specialized version in a child.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal:
    def make_sound(self):
        print("Generic animal sound") # Parent's method

class Dog(Animal):
    def make_sound(self): # Overriding the parent's make_sound
        print("Woof woof!") # Child's specific implementation

# Output:
# my_animal = Animal()
# my_animal.make_sound() # Generic animal sound

# my_dog = Dog()
# my_dog.make_sound()    # Woof woof!

Supercharging with super()! 🚀

Sometimes, you don’t want to completely replace the parent’s method but extend it. That’s where super() comes in! It allows the child class to call the parent class’s method implementation before or after adding its own logic. It’s like saying, “Let the parent do its part, then I’ll add my touch.”

1
2
3
4
5
6
7
8
9
10
class Cat(Animal):
    def make_sound(self):
        super().make_sound() # Calls the parent's make_sound() first
        print("Meow!")       # Then adds its own unique sound

# Output:
# my_cat = Cat()
# my_cat.make_sound()
# Generic animal sound
# Meow!

Polymorphism: Many Forms, One Action! ✨

Method overriding is key to polymorphism (meaning “many forms”). It allows objects of different classes to be treated as objects of a common base type. When you call an overridden method on these objects, each object performs its own specific version of that method. This makes your code flexible and reusable.

1
2
3
4
5
6
7
8
9
10
animals = [Animal(), Dog(), Cat()]

for animal in animals:
    animal.make_sound() # Each object responds with its specific sound

# Output:
# Generic animal sound
# Woof woof!
# Generic animal sound
# Meow!

Class Hierarchy Visualisation 🖼️

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#ff4f81','primaryTextColor':'#fff','primaryBorderColor':'#c43e3e','lineColor':'#e67e22','secondaryColor':'#6b5bff','tertiaryColor':'#ffd700'}}}%%
classDiagram
    class Animal {
        + make_sound()
    }
    class Dog {
        + make_sound()
    }
    class Cat {
        + make_sound()
    }

    Animal <|-- Dog : inherits
    Animal <|-- Cat : inherits
    
    style Animal fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14
    style Dog fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14
    style Cat fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14

🎯 Hands-On Assignment

💡 Project: Employee Management System with Inheritance (Click to expand)

🚀 Your Challenge:

Build an Employee Management System using inheritance to model different employee types. Your system should demonstrate single, multiple, and multilevel inheritance with proper use of super() and method overriding. 👨‍💼👩‍💼

📋 Requirements:

Part 1: Base Employee Class

  • Create an Employee base class with attributes: name, emp_id, salary
  • Add class variable company_name shared by all employees
  • Implement methods:
    • __init__(name, emp_id, salary) - initialize employee
    • get_details() - return employee information
    • calculate_bonus() - base bonus calculation (10% of salary)
  • Use @classmethod to get/set company name

Part 2: Specialized Employee Types (Single Inheritance)

  • Create Developer class inheriting from Employee
    • Add attributes: programming_languages (list), projects_completed
    • Override calculate_bonus() - add $500 per completed project
    • Use super() to call parent's __init__ and calculate_bonus()
  • Create Manager class inheriting from Employee
    • Add attributes: team_size, department
    • Override calculate_bonus() - add $200 per team member
    • Add method conduct_meeting()

Part 3: Multiple Inheritance with Mixins

  • Create Benefits mixin class with:
    • health_insurance = True
    • get_benefits() - returns available benefits
  • Create RemoteWorker mixin class with:
    • remote_allowance = 1000
    • get_remote_perks() - returns remote benefits
  • Create SeniorDeveloper class inheriting from Developer, Benefits, RemoteWorker
    • Override calculate_bonus() to include remote allowance
    • Use super() properly to call all parent methods
    • Print MRO using __mro__ to understand method resolution

Part 4: Multilevel Inheritance

  • Create TechLead class inheriting from SeniorDeveloper
    • Add attributes: mentees (list of developers)
    • Override calculate_bonus() - add $300 per mentee
    • Demonstrate proper use of super() across multiple levels

💡 Implementation Hints:

  • Always use super().__init__() in child classes to initialize parent attributes 🎯
  • When overriding methods, use super().method_name() to extend (not replace) parent behavior ⚙️
  • For multiple inheritance, order base classes carefully: specific before general 📊
  • Use isinstance() and issubclass() to check relationships 🔍
  • Print ClassName.__mro__ to understand Method Resolution Order 🧠
  • Document IS-A relationships (Developer IS-A Employee) 📝
  • Use mixins for cross-cutting concerns like Benefits and RemoteWorker 🔧

Example Input/Output:

# Creating employees
dev = Developer("Alice", "D001", 80000, ["Python", "JavaScript"], 5)
mgr = Manager("Bob", "M001", 100000, 10, "Engineering")
senior_dev = SeniorDeveloper("Carol", "SD001", 120000, ["Python", "Go", "Rust"], 12)
tech_lead = TechLead("David", "TL001", 150000, ["Python", "Java"], 15, ["Alice", "Eve"])

# Display details
print(dev.get_details())
# Output: Employee: Alice (D001), Salary: $80000, Languages: Python, JavaScript

# Calculate bonuses
print(f"Alice's bonus: ${dev.calculate_bonus()}")
# Output: Alice's bonus: $10500 (Base 8000 + 5 projects * 500)

print(f"Bob's bonus: ${mgr.calculate_bonus()}")
# Output: Bob's bonus: $12000 (Base 10000 + 10 members * 200)

# Multiple inheritance demonstration
print(senior_dev.get_benefits())
# Output: Health Insurance, Dental, Vision

print(senior_dev.get_remote_perks())
# Output: Remote allowance: $1000, Home office setup

# Check MRO
print(f"SeniorDeveloper MRO: {SeniorDeveloper.__mro__}")
# Output: Shows method resolution order

# Multilevel inheritance
print(f"David's bonus: ${tech_lead.calculate_bonus()}")
# Output: David's bonus: $27100 (includes all levels of bonuses)

# Type checking
print(f"Is senior_dev a Developer? {isinstance(senior_dev, Developer)}")
# Output: True
print(f"Is Developer a subclass of Employee? {issubclass(Developer, Employee)}")
# Output: True

🌟 Bonus Challenges:

  • Add Intern class with limited benefits and fixed salary 🎓
  • Implement __str__ and __repr__ for better object representation 🎭
  • Create Department class to manage multiple employees 🏢
  • Add method resolution order visualization showing inheritance hierarchy 📊
  • Implement promote() method to upgrade employee types 📈
  • Handle edge cases (negative salary, invalid team size) with proper validation ⚠️
  • Add Contractor class with different payment structure 💼
  • Create unit tests for all inheritance relationships and method overrides 🧪

Submission Guidelines:

  • Demonstrate all three inheritance types (single, multiple, multilevel) ✅
  • Show proper use of super() in all child classes 🔗
  • Print and explain MRO for classes with multiple inheritance 🧠
  • Include examples of method overriding and extension 🔄
  • Test with multiple employee objects to verify independence 🧪
  • Document all IS-A relationships in comments 📝
  • Share your complete code in the comments with sample output 💬

Share Your Solution! 💬

Completed the project? Post your code in the comments below! Show us how you mastered inheritance and share any creative features you added! 🚀


Conclusion

Well, that’s a wrap for today! We truly hope you found something interesting or useful in our post. Your thoughts and ideas are super important to us. 🙏 Did anything catch your eye? Do you have your own experiences or tips to add? Please don’t be shy! We’d absolutely love to hear from you. Share your comments, feedback, or suggestions in the section below. Let’s keep the conversation going! 👇😊

This post is licensed under CC BY 4.0 by the author.