Polymorphism in Python
Polymorphism in Python
Polymorphism is a key concept in Object-Oriented Programming (OOP) that allows objects of different classes to be treated as objects of a common superclass. The term polymorphism comes from Greek words meaning "many shapes," and it allows for methods to be used interchangeably, even if they have different implementations.
In Python, polymorphism can be achieved in several ways, including method overriding and method overloading (though Python primarily supports method overriding).
Types of Polymorphism in Python
Method Overriding (Runtime Polymorphism)
Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. This is the most common form of polymorphism in Python.
Method Overloading (Not directly supported in Python)
Python does not support method overloading directly (i.e., defining multiple methods with the same name but different signatures). However, you can achieve similar functionality using default arguments or variable-length arguments.
Example of Method Overriding (Runtime Polymorphism)
Method overriding is when a subclass provides a specific implementation of a method that is already defined in the parent class. Here is an example:
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 instances of the classesanimal = Animal()dog = Dog()cat = Cat()# Call the speak method (this is polymorphism in action)animal.speak() # Output: Animal speaksdog.speak() # Output: Dog barkscat.speak() # Output: Cat meowsIn this example, the speak() method is overridden in both the Dog and Cat subclasses. Even though the same method name is used (speak), the output differs depending on the object type. This is polymorphism at work.
Polymorphism Using Inheritance
In Python, polymorphism is often used with inheritance. You can create a common interface (in the form of a superclass) and provide specific implementations of that interface in the subclasses.
class Shape: def area(self): pass # Base method, to be overridden by subclassesclass Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius * self.radiusclass Square(Shape): def __init__(self, side): self.side = side def area(self): return self.side * self.side# Create objectscircle = Circle(5)square = Square(4)# Call area methodprint("Circle Area:", circle.area()) # Output: Circle Area: 78.5print("Square Area:", square.area()) # Output: Square Area: 16In this case, both Circle and Square inherit from the Shape class and provide their own implementation of the area() method. Even though both objects are of different types, you can treat them as Shape objects and call the area() method on them, which will execute the appropriate version based on the object type. This is polymorphism.
Polymorphism with __str__ Method (Custom String Representation)
Polymorphism is also evident in Python’s built-in __str__ method, which can be overridden to provide custom string representations of objects.
class Car: def __init__(self, model, year): self.model = model self.year = year def __str__(self): return f"Car Model: {self.model}, Year: {self.year}"class Bike: def __init__(self, brand, year): self.brand = brand self.year = year def __str__(self): return f"Bike Brand: {self.brand}, Year: {self.year}"# Create instancescar = Car("Toyota", 2021)bike = Bike("Yamaha", 2019)# Print the objects (polymorphism in action)print(car) # Output: Car Model: Toyota, Year: 2021print(bike) # Output: Bike Brand: Yamaha, Year: 2019In this example, both the Car and Bike classes implement the __str__() method, which allows us to define a custom string representation of the objects. When we call print() on these objects, the appropriate __str__() method is called, depending on the type of the object.
Polymorphism with Duck Typing
In Python, polymorphism can also be achieved using duck typing, which means that an object’s suitability for use is determined by the presence of certain methods and properties, rather than by its actual type. This allows for more flexibility in how objects are handled.
For example, if an object behaves like a file (i.e., has methods like read() and write()), it can be treated as a file, regardless of its actual class.
class Dog: def speak(self): print("Woof")class Human: def speak(self): print("Hello")def make_speak(obj): obj.speak()# Objects of different typesdog = Dog()human = Human()# Polymorphism through duck typingmake_speak(dog) # Output: Woofmake_speak(human) # Output: HelloIn this case, both the Dog and Human classes have a speak() method. The make_speak() function does not care about the actual type of the object. It just calls the speak() method on whatever object is passed, demonstrating polymorphism via duck typing.
Conclusion
Polymorphism is a powerful concept in Python that allows you to write flexible and reusable code. Whether it's method overriding, using inheritance, or leveraging Python's duck typing, polymorphism makes it possible to treat different objects in a unified way, with the correct behavior being invoked based on the object type. This allows you to design more abstract and generalized systems.