Python Polymorphism

Polymorphism is an essential concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common superclass. Python supports polymorphism through method overriding in inheritance, operator overloading, and duck typing.

Polymorphism enables flexibility and extensibility in code, making it easier to implement functionality that applies to different objects with a shared interface or common behavior.

1. Basic Concept of Polymorphism

Polymorphism comes from the Greek words "poly" (many) and "morph" (forms), meaning something that exists in different forms. In Python, this means using a single method or operator to work with multiple types of objects or classes.

2. Polymorphism with Functions and Objects

Example: A function that uses polymorphism to operate on different object types. Here, `Dog` and `Cat` have a `sound()` method, allowing us to use them interchangeably.
class Dog:
    def sound(self):
        return "Woof!"

class Cat:
    def sound(self):
        return "Meow!"

def make_sound(animal):
    return animal.sound()

dog = Dog()
cat = Cat()
print(make_sound(dog))
print(make_sound(cat))

Output:
Woof!
Meow!

Explanation: Here, the `make_sound()` function operates on both `Dog` and `Cat` objects, demonstrating polymorphism since both classes implement a `sound()` method.

3. Polymorphism in Class Methods

Method Overriding allows subclasses to have their own specific implementation of methods that are already defined in the superclass.

Example of Method Overriding:
class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    return animal.speak()

dog = Dog()
cat = Cat()
print(animal_sound(dog))
print(animal_sound(cat))

Output:
Woof!
Meow!

Explanation: Here, `Dog` and `Cat` classes override the `speak` method from the `Animal` superclass. The `animal_sound` function can then call `speak` on any object of type `Animal`, allowing polymorphism in action.

4. Polymorphism with Abstract Classes

Abstract classes define methods that must be implemented by subclasses. They are often used to ensure that specific methods exist in different classes that share a common superclass.
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius * self.radius

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

def calculate_area(shape):
    return shape.area()

circle = Circle(5)
rectangle = Rectangle(4, 6)
print(calculate_area(circle))
print(calculate_area(rectangle))

Output:
78.53975
24

Explanation: Here, `Shape` is an abstract base class with an abstract method `area`. Both `Circle` and `Rectangle` implement `area`, which allows us to calculate areas using `calculate_area` polymorphically.

5. Operator Overloading

Python allows operators to be overloaded using special methods, enabling them to work with custom objects.

Example of Operator Overloading:
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 4)
v2 = Vector(3, 5)
print(v1 + v2)

Output:
Vector(5, 9)

Explanation: By defining the `__add__` method, we overloaded the `+` operator to add `Vector` objects. This is another form of polymorphism, allowing `+` to work on a custom data type.

6. Duck Typing in Python

Python relies on duck typing, where an object’s compatibility with an operation depends on the methods it implements rather than its class.

Example of Duck Typing:
class Bird:
    def fly(self):
        return "Flying high!"

class Airplane:
    def fly(self):
        return "Jet engines roaring!"

def take_off(entity):
    return entity.fly()

bird = Bird()
airplane = Airplane()
print(take_off(bird))
print(take_off(airplane))

Output:
Flying high!
Jet engines roaring!

Explanation: In this example, `Bird` and `Airplane` both have a `fly` method. The `take_off` function doesn't check the object’s class but only that it has a `fly` method, illustrating duck typing.

7. Polymorphism with Built-in Functions

Python’s built-in functions like `len`, `max`, and `min` demonstrate polymorphism, as they can work on various data types.
print(len("hello"))         # Length of a string
print(len([1, 2, 3, 4]))    # Length of a list
print(max(5, 10, 15))       # Max among numbers
print(max("a", "z", "m"))   # Max among characters

Output:
5
4
15
z

Explanation: Here, `len` and `max` work on different data types due to polymorphism, allowing flexibility in how these functions are used.

8. Polymorphism in Real-World Scenarios

Polymorphism is especially useful in cases where multiple classes share a common interface but provide unique implementations. Consider an application where different media (audio, video) need to play content but use distinct classes and methods.
class Audio:
    def play(self):
        return "Playing audio"

class Video:
    def play(self):
        return "Playing video"

def start_media(media):
    return media.play()

audio = Audio()
video = Video()
print(start_media(audio))
print(start_media(video))

Output:
Playing audio
Playing video

Explanation: Here, `Audio` and `Video` classes both implement a `play` method. The `start_media` function can call `play` on either type, demonstrating polymorphism for a real-world media player application.

Summary

Polymorphism in Python offers multiple approaches for managing objects and operations with flexibility and efficiency. With techniques like method overriding, operator overloading, and duck typing, Python developers can create adaptable code that works smoothly across various classes and types.

Previous: Python Encapsulation | Next: Operator Overloading

<
>