Python Polymorphism
Python Polymorphism
Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows one method or operator to work in different ways depending on the type of object it is acting upon. The term comes from Greek, meaning “many shapes.” In Python, polymorphism is achieved through method overriding and method overloading.
Types of Polymorphism in Python:
- Method Overriding (Runtime Polymorphism)
- Method Overloading (Compile-time Polymorphism, not directly supported in Python)
- Operator Overloading
1. Method Overriding (Runtime Polymorphism)
Method overriding occurs when a child class provides a specific implementation for a method that is already defined in its parent class. The method in the child class has the same name and signature as the method in the parent class, but the implementation can be different. This allows the child class to provide its own behavior while maintaining the interface defined in the parent class.
Example of Method Overriding:
class Animal:
def speak(self):
print("Animal speaks")
class Dog(Animal):
def speak(self):
print("Dog barks")
class Cat(Animal):
def speak(self):
print("Cat meows")
# Create objects
animals = [Dog(), Cat()]
# Iterate over the list and call the speak method
for animal in animals:
animal.speak() # This demonstrates polymorphism
Output:
Dog barks
Cat meows
In this example:
- Both
DogandCatclasses override thespeak()method from theAnimalclass. - The correct
speak()method is called based on the type of object, even though the method callanimal.speak()is the same. This is runtime polymorphism because the method that gets executed depends on the object type at runtime.
2. Method Overloading (Compile-time Polymorphism)
In languages like C++ and Java, method overloading allows you to define multiple methods with the same name but with different parameters (different types or a different number of arguments). However, Python does not directly support method overloading in the same way.
In Python, you can achieve method overloading behavior by using default arguments or variable-length arguments (*args and **kwargs), but it does not provide strict compile-time overloading.
Example of Overloading Using Default Arguments:
class Calculator:
def add(self, a, b=0):
return a + b
# Create an object of Calculator
calc = Calculator()
# Calling add with one argument (default b=0)
print(calc.add(10)) # Output: 10
# Calling add with two arguments
print(calc.add(10, 5)) # Output: 15
Example of Overloading Using *args:
class Calculator:
def add(self, *args):
return sum(args)
# Create an object of Calculator
calc = Calculator()
# Calling add with variable arguments
print(calc.add(10)) # Output: 10
print(calc.add(10, 5)) # Output: 15
print(calc.add(1, 2, 3, 4)) # Output: 10
In these examples:
- The
addmethod can handle different numbers of arguments, simulating method overloading.
3. Operator Overloading
Operator overloading allows you to define the behavior of operators (like +, -, *, etc.) for user-defined classes. By overriding special methods, you can change how operators work with objects of your custom class.
For example, you can override the __add__ method to define how the + operator behaves when used with instances of a class.
Example of Operator Overloading:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# Overload the + operator to add two Point objects
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 Point objects
p1 = Point(1, 2)
p2 = Point(3, 4)
# Use the overloaded + operator
p3 = p1 + p2 # This calls p1.__add__(p2)
print(p3) # Output: Point(4, 6)
In this example:
- The
__add__method is overridden to allow the addition of twoPointobjects using the+operator. - The result of
p1 + p2is a newPointobject with the coordinates(4, 6).
4. Polymorphism with Built-in Functions
Python also allows polymorphism to occur in the context of built-in functions, especially when functions accept various types of arguments. For instance, the len() function can be used to calculate the length of a string, list, tuple, or dictionary, and it works differently depending on the type of object passed.
Example with len():
print(len("hello")) # Output: 5 (String length)
print(len([1, 2, 3])) # Output: 3 (List length)
print(len((1, 2, 3))) # Output: 3 (Tuple length)
print(len({"a": 1, "b": 2})) # Output: 2 (Dictionary length)
The len() function works with different types of objects, demonstrating polymorphism in action.
5. Polymorphism in Inheritance
When using inheritance, polymorphism allows you to call the same method on objects of different classes, with each class providing a different implementation of that method. This is known as dynamic polymorphism.
Example of Polymorphism in Inheritance:
class Animal:
def sound(self):
print("Some generic animal sound")
class Dog(Animal):
def sound(self):
print("Bark")
class Cat(Animal):
def sound(self):
print("Meow")
def make_sound(animal):
animal.sound() # Polymorphic behavior
# Create objects
dog = Dog()
cat = Cat()
# Call the same function with different object types
make_sound(dog) # Output: Bark
make_sound(cat) # Output: Meow
In this example:
- The
make_soundfunction takes anAnimalobject but behaves differently depending on whether it’s aDogorCatobject. - This shows how polymorphism allows the same function to handle different types of objects and execute different code.
Summary of Polymorphism in Python:
- Method Overriding: Allows child classes to provide their own implementation of a method defined in the parent class.
- Method Overloading: Python doesn’t directly support method overloading, but you can simulate it using default arguments or variable-length arguments (
*args). - Operator Overloading: You can change the behavior of operators for custom classes by defining special methods like
__add__,__sub__, etc. - Polymorphism in Inheritance: The ability for objects of different classes to be treated as objects of a common parent class, but with each class providing its own implementation of methods.
Polymorphism allows for more flexible and reusable code by letting you define methods in a generic way while enabling different implementations for different objects.