Inheritance Polymorphism
Definition
Inheritance polymorphism refers to the ability of a subclass to provide a specific implementation of methods that are defined in its superclass. This allows you to treat objects of different subclasses uniformly, even though their underlying implementations of methods might differ.
Purpose
The main goal of inheritance polymorphism is to enable code that operates on objects of a base class to work with objects of derived classes without knowing the specific subclass. This promotes code reusability and flexibility.
Detailed Examples
Example 1: Shapes with Different Behaviors
Consider a scenario with geometric shapes where each shape calculates its area differently.
class Shape: def area(self): raise NotImplementedError("Subclasses should implement this!") class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): import math return math.pi * self.radius ** 2 def print_area(shape): print(f"The area is: {shape.area()}") # Using inheritance polymorphism shapes = [Rectangle(3, 4), Circle(5)] for shape in shapes: print_area(shape)
Explanation
- Base Class (Shape): Defines a method area that must be implemented by any subclass. This serves as a contract for derived classes.
- Subclasses (Rectangle, Circle): Provide specific implementations of the area method. Each subclass handles the computation of the area according to its own attributes.
- Function print_area: Accepts any object of type Shape and calls its area method. Thanks to inheritance polymorphism, print_area can work with any subclass of Shape without knowing its specific type.
Example 2: Employee Payroll System
Imagine a payroll system where you have different types of employees, such as full-time and part-time employees. Each type calculates salary differently.
class Employee: def calculate_salary(self): raise NotImplementedError("Subclasses should implement this!") class FullTimeEmployee(Employee): def __init__(self, base_salary): self.base_salary = base_salary def calculate_salary(self): return self.base_salary class PartTimeEmployee(Employee): def __init__(self, hourly_rate, hours_worked): self.hourly_rate = hourly_rate self.hours_worked = hours_worked def calculate_salary(self): return self.hourly_rate * self.hours_worked def print_salary(employee): print(f"Salary: {employee.calculate_salary()}") # Using inheritance polymorphism employees = [FullTimeEmployee(5000), PartTimeEmployee(20, 80)] for employee in employees: print_salary(employee)
Explanation
- Base Class (Employee): Defines a method calculate_salary that each subclass must implement. This provides a common interface for calculating salaries.
- Subclasses (FullTimeEmployee, PartTimeEmployee): Implement the calculate_salary method in different ways according to their specific compensation structures.
- Function print_salary: Can handle any Employee type and print the salary, demonstrating how inheritance polymorphism allows for uniform handling of different employee types.
Advantages of Inheritance Polymorphism
- Code Reusability: By using a common base class interface, you can write code that works with any subclass, reducing redundancy and promoting reuse.
- Flexibility: It allows you to write more generic code that can operate on objects of different types, which can be extended with new subclasses without changing existing code.
- Maintainability: Changes in the base class methods or interface propagate to derived classes, reducing the need for changes in code that uses these classes.
- Extensibility: You can easily extend your application by adding new subclasses, provided they adhere to the interface defined by the base class.
Practical Use Cases
Use Case 1: UI Frameworks
In user interface frameworks, you might have a base class Widget with methods like draw and resize. Various UI elements such as Button, Textbox, and Label would inherit from Widget and provide specific implementations of these methods.
Use Case 2: Data Processing Pipelines
In a data processing pipeline, you might have a base class DataProcessor with a method process. Subclasses like CSVProcessor, JSONProcessor, and XMLProcessor would implement the process method to handle different data formats.
Key Points
- Method Overriding: Subclasses override methods defined in the base class to provide specific behavior.
- Uniform Interface: Code that uses the base class can operate on any subclass instance, making the code more flexible and easier to extend.
- Contract Enforcement: The base class defines a contract (e.g., methods that must be implemented), ensuring that all subclasses adhere to this contract.
In summary, inheritance polymorphism in Python allows you to use a base class interface to work with different subclasses in a uniform way. This facilitates code reuse, flexibility, and maintenance, making it a powerful tool in object-oriented programming.