Python Inheritance and Polymorphism: A Comprehensive Guide
In the world of Object-Oriented Programming (OOP), Inheritance and Polymorphism are two pillars that allow developers to write reusable, scalable, and efficient code. While they might sound like complex biological terms, in Python, they are straightforward tools that help us model real-world relationships between objects.
What is Inheritance?
Inheritance allows a class (called a Child or Derived class) to acquire the properties and methods of another class (called a Parent or Base class). This promotes code reusability—you don't have to rewrite the same logic for similar objects.
Basic Syntax
class Parent:
def speak(self):
print("Parent speaking")
class Child(Parent):
def play(self):
print("Child playing")
# The Child class can access Parent methods
obj = Child()
obj.speak() # Output: Parent speaking
The super() Function
The super() function is used to call methods from the parent class inside the child class. This is particularly useful when you want to extend the functionality of the parent's constructor (__init__).
class Employee:
def __init__(self, name):
self.name = name
class Developer(Employee):
def __init__(self, name, language):
super().__init__(name) # Inherit name from Employee
self.language = language
Visualizing Inheritance Structure
[ Base Class: Animal ]
^
|
[ Derived Class: Dog ] ----> (Inherits name, age)
[ Derived Class: Cat ] ----> (Inherits name, age)
What is Polymorphism?
The word Polymorphism means "many forms." In programming, it refers to the ability of different classes to be treated as instances of the same general class through the same interface. Most commonly, this is achieved through Method Overriding.
Method Overriding
Method overriding occurs when a child class provides a specific implementation of a method that is already defined in its parent class.
class Shape:
def draw(self):
print("Drawing a generic shape")
class Circle(Shape):
def draw(self):
print("Drawing a Circle")
class Square(Shape):
def draw(self):
print("Drawing a Square")
shapes = [Circle(), Square()]
for s in shapes:
s.draw() # Each object responds differently to the same call
Real-World Use Case: Payment Systems
Imagine an e-commerce application. You have a base class Payment and various specific payment methods like CreditCard, PayPal, and Bitcoin. Each specific class inherits from Payment but implements its own process_payment() logic.
- Inheritance: All payment types share common attributes like
amountandtransaction_id. - Polymorphism: The checkout system calls
process_payment()without needing to know if it is a card or crypto; the object knows how to handle itself.
Common Mistakes to Avoid
- Forgetting self: Always remember that the first argument of any instance method must be
self. - Hardcoding Parent Names: Avoid using
ParentClassName.__init__(self). Usesuper().__init__()instead to keep your code flexible for multiple inheritance. - Over-complicating Hierarchy: Don't create deep inheritance trees (e.g., A -> B -> C -> D -> E). This makes the code hard to debug and maintain.
- Ignoring Method Signatures: When overriding a method, ensure the parameters match the parent's method signature to avoid unexpected errors.
Interview Notes and Technical Tips
- MRO (Method Resolution Order): Python uses the C3 Linearization algorithm to determine the order in which classes are searched for a method. You can view this using
ClassName.mro(). - Duck Typing: Python follows the philosophy: "If it walks like a duck and quacks like a duck, it’s a duck." This means polymorphism in Python doesn't always require formal inheritance; as long as an object has the required method, Python will execute it.
- isinstance() vs type(): Use
isinstance(obj, ClassName)to check if an object is an instance of a class or its subclasses. Usetype()only if you need an exact class match. - Abstract Base Classes (ABC): For advanced designs, use the
abcmodule to define templates that child classes must implement.
Summary
Inheritance allows us to create a "is-a" relationship (e.g., a Car is a Vehicle), enabling code reuse. Polymorphism allows us to use a unified interface for different underlying forms (data types). Mastering these concepts is essential for moving from a beginner to an advanced Python developer and is a core requirement for building professional software architectures.
To deepen your understanding, try exploring our previous lesson on Classes and Objects or move forward to Encapsulation and Abstraction to complete your OOP journey.