Polymorphism is a key concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common super class.
In Python, polymorphism enables the use of a unified interface for objects of different types, making it possible to write more generic and reusable code.
What is Polymorphism?
Polymorphism means “many forms” and refers to the ability of a function, method, or object to behave in multiple ways depending on the context. Python supports two types of polymorphism:
Compile-time Polymorphism (Function Overloading): Python does not directly support this feature, but it can be simulated.
Run-time Polymorphism (Method Overriding): Achieved through inheritance, where child classes can override methods of parent classes.
1. Polymorphism with Functions
You can define a function that can accept different types of objects or arguments, and it will behave differently depending on the object passed.
Example: Polymorphism with Built-in Functions
# Polymorphism with len() function print(len("Hello")) # Output: 5 (length of string) print(len([1, 2, 3, 4, 5])) # Output: 5 (length of list) print(len({"name": "Alice", "age": 25})) # Output: 2 (number of key-value pairs in the dictionary)
In this example, the len() function works with different types (string, list, and dictionary), showing polymorphism in action.
2. Polymorphism with User-defined Functions
You can define your own functions that can operate on different data types, allowing for a more generalized and flexible design.
Example: Polymorphic Function for Different Shapes
# Define a function that calculates the area of different shapes def area(shape): return shape.area() # Define classes for different shapes class Circle: def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius ** 2 class Rectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height # Create objects of different shapes circle = Circle(5) rectangle = Rectangle(4, 6) # Call the area function for both shapes print(area(circle)) # Output: 78.5 print(area(rectangle)) # Output: 24
Here, the area() function works with both Circle and Rectangle objects, even though they are different types. This is an example of polymorphism where the same function works differently for different objects.
3. Polymorphism with Class Methods
In Python, you can implement polymorphism through class inheritance and method overriding. This is known as runtime polymorphism. A method in the parent class can be overridden in the child class, and the appropriate method is called depending on the type of the object.
Example: Polymorphism with Method Overriding
# Parent class class Animal: def speak(self): print("The animal makes a sound.") # Child classes class Dog(Animal): def speak(self): print("The dog barks.") class Cat(Animal): def speak(self): print("The cat meows.") # Create objects of different classes dog = Dog() cat = Cat() # Call the speak method for each object dog.speak() # Output: The dog barks. cat.speak() # Output: The cat meows.
In this example, the speak() method is defined in the Animal class, but both Dog and Cat classes override it to provide their own implementation. When calling speak(), the method appropriate to the object's class is executed, demonstrating polymorphism.
4. Polymorphism with Inheritance
Polymorphism is closely tied to inheritance, where child classes inherit the properties and behaviors of parent classes but can also override them.
Example: Polymorphism in an Inheritance Hierarchy
# Parent class class Vehicle: def description(self): return "This is a vehicle." # Child class for Car class Car(Vehicle): def description(self): return "This is a car." # Child class for Bike class Bike(Vehicle): def description(self): return "This is a bike." # Function that accepts any vehicle type def show_description(vehicle): print(vehicle.description()) # Create instances of Car and Bike car = Car() bike = Bike() # Call the function with different vehicle types show_description(car) # Output: This is a car. show_description(bike) # Output: This is a bike.
In this example, the description() method is overridden in both Car and Bike, and the show_description() function demonstrates polymorphism by calling the appropriate method based on the object passed.
5. Polymorphism with Abstract Classes
Polymorphism is often implemented using abstract base classes (ABCs) in Python. An abstract class defines methods that must be implemented by child classes, ensuring polymorphism while enforcing method implementation.
Example: Polymorphism with Abstract Base Class
from abc import ABC, abstractmethod # Abstract base class class Animal(ABC): @abstractmethod def speak(self): pass # Child class Dog implements the abstract method class Dog(Animal): def speak(self): return "Woof!" # Child class Cat implements the abstract method class Cat(Animal): def speak(self): return "Meow!" # Function to demonstrate polymorphism def animal_speak(animal): print(animal.speak()) # Create instances of Dog and Cat dog = Dog() cat = Cat() # Call the polymorphic function animal_speak(dog) # Output: Woof! animal_speak(cat) # Output: Meow!
Here, the Animal class is abstract, and both Dog and Cat must implement the speak() method. The animal_speak() function demonstrates polymorphism by accepting different subclasses of Animal and calling their respective speak() methods.
6. Polymorphism with Multiple Classes
You can achieve polymorphism across multiple unrelated classes, provided they have methods with the same name. Python's dynamic nature allows you to use polymorphism without inheritance.
Example: Polymorphism Across Unrelated Classes
class Dog: def speak(self): return "Woof!" class Cat: def speak(self): return "Meow!" class Cow: def speak(self): return "Moo!" # Function that works with different animals def animal_speak(animal): print(animal.speak()) # Create instances of different animals dog = Dog() cat = Cat() cow = Cow() # Call the polymorphic function animal_speak(dog) # Output: Woof! animal_speak(cat) # Output: Meow! animal_speak(cow) # Output: Moo!
In this example, Dog, Cat, and Cow are unrelated classes, but they all implement the speak() method. The animal_speak() function demonstrates polymorphism by treating different objects with the same interface in a unified way.
7. Polymorphism with Operator Overloading
Python supports operator overloading, which allows you to define custom behavior for operators (like +, -, *, etc.) in user-defined classes.
Example: Polymorphism with Operator Overloading
class Point: def __init__(self, x, y): self.x = x self.y = y # Overload the + operator to add two points def __add__(self, other): return Point(self.x + other.x, self.y + other.y) def __repr__(self): return f"Point({self.x}, {self.y})" # Create two points p1 = Point(2, 3) p2 = Point(4, 5) # Add the two points using the overloaded + operator result = p1 + p2 print(result) # Output: Point(6, 8)
In this example, the + operator is overloaded using the __add__() method to add two Point objects. This is a form of polymorphism where the same operator (+) works differently for user-defined objects.
Summary
Polymorphism is the ability of different objects to respond to the same method or function in different ways.
Method Overriding is a common way to achieve polymorphism, where child classes provide their own implementation of a parent class method.
Polymorphism allows for generic functions and methods that can handle objects of different classes, increasing code reusability and flexibility.
Abstract classes and interfaces can enforce polymorphism by requiring child classes to implement certain methods.
Operator overloading is a type of polymorphism where operators behave differently based on the operands.
By mastering polymorphism, you can write more flexible and maintainable Python code that can handle a variety of object types in a unified manner.