Simple Terms

Polymorphism in Python means that different objects can be treated in a similar way, even if they belong to different classes. It's like using a single remote control to operate various electronic devices like a TV, DVD player, and stereo, without worrying about their internal workings. Polymorphism allows you to write flexible code that works with multiple object types, making it easier to reuse and maintain your code.

Introduction

Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common superclass. It enables a single interface to represent different data types or objects and allows them to be used interchangeably. In Python, polymorphism is achieved through method overriding and duck typing.

Method Overriding:

In Python, method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. When a method is called on an object, Python first looks for that method in the class of the object. If it doesn't find the method in the current class, it looks for it in the superclass until it reaches the root class (usually the object class). The method found in the lowest subclass in the inheritance chain will be executed.

Here's an example of method overriding in Python:

class Animal:
    def sound(self):
        return "Animal makes a sound."

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

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

# Function that uses polymorphism
def make_sound(animal):
    return animal.sound()

# Create objects of different classes
dog = Dog()
cat = Cat()

# Use polymorphism to call the 'sound' method on different objects
print(make_sound(dog))  # Output: "Woof!"
print(make_sound(cat))  # Output: "Meow!"

In this example, both the Dog and Cat classes inherit from the Animal class. Despite having different sound methods in each subclass, the make_sound function can treat them as Animal objects and call their specific implementations.

Duck Typing

Python is dynamically typed, which means the type of an object is determined at runtime. Duck typing takes advantage of this dynamic nature and focuses on an object's behavior rather than its type. The term "duck typing" comes from the saying, "If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck." In other words, as long as an object implements a certain behavior or method, it can be treated as a member of a particular class or type.

Here's a simple example of duck typing in Python:

class Duck:
    def quack(self):
        return "Quack, quack!"

class Robot:
    def quack(self):
        return "Beep, boop!"

def duck_or_robot_sound(something):
    return something.quack()

duck = Duck()
robot = Robot()

print(duck_or_robot_sound(duck))    # Output: "Quack, quack!"
print(duck_or_robot_sound(robot))   # Output: "Beep, boop!"

In this example, the duck_or_robot_sound function doesn't care about the specific type of the object it receives. It simply assumes that the object passed as an argument has a quack method and calls it, demonstrating duck typing.

In summary, polymorphism in Python allows different objects to be treated uniformly through method overriding and duck typing. This flexibility enhances code reusability and makes it easier to work with different types of objects in a consistent manner.