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! 💡
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
CarIS-AVehicle.
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
DogIS-AAnimal. - A
SedanIS-ACar. 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
🦸♀️ 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.
Ddoesn’t haveshow_origin.Bdoesn’t haveshow_origin.Cdoes haveshow_origin, soC’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
Parentclass automatically gets all features from itsGrandparent. - Consequently, the
Childclass inherits all features from itsParent, and indirectly also from theGrandparent!
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
Employeebase class with attributes:name,emp_id,salary - Add class variable
company_nameshared by all employees - Implement methods:
__init__(name, emp_id, salary)- initialize employeeget_details()- return employee informationcalculate_bonus()- base bonus calculation (10% of salary)
- Use
@classmethodto get/set company name
Part 2: Specialized Employee Types (Single Inheritance)
- Create
Developerclass inheriting fromEmployee- Add attributes:
programming_languages(list),projects_completed - Override
calculate_bonus()- add $500 per completed project - Use
super()to call parent's__init__andcalculate_bonus()
- Add attributes:
- Create
Managerclass inheriting fromEmployee- Add attributes:
team_size,department - Override
calculate_bonus()- add $200 per team member - Add method
conduct_meeting()
- Add attributes:
Part 3: Multiple Inheritance with Mixins
- Create
Benefitsmixin class with:health_insurance = Trueget_benefits()- returns available benefits
- Create
RemoteWorkermixin class with:remote_allowance = 1000get_remote_perks()- returns remote benefits
- Create
SeniorDeveloperclass inheriting fromDeveloper,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
- Override
Part 4: Multilevel Inheritance
- Create
TechLeadclass inheriting fromSeniorDeveloper- Add attributes:
mentees(list of developers) - Override
calculate_bonus()- add $300 per mentee - Demonstrate proper use of
super()across multiple levels
- Add attributes:
💡 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()andissubclass()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
Internclass with limited benefits and fixed salary 🎓 - Implement
__str__and__repr__for better object representation 🎭 - Create
Departmentclass 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
Contractorclass 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! 👇😊