๐Ÿ“ Polymorphism in Python ๐Ÿ#

Polymorphism is an OOP concept where the same interface can lead to different implementations across multiple classes. It allows you to write flexible, extensible code that can handle different object types in a uniform way.

What Is Polymorphism?#

  1. Definition: The ability of objects of different classes to respond to the same function or method call in different ways.

  1. Purpose: Write generalized code that works with diverse objects, without needing type checks or separate logic paths.

Polymorphism with Class Methods#

Example: A speak() Method in Different Animal Classes#

class Animal:
    def speak(self):
        return "Some generic sound"


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


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


def make_sound(animal):
    print(animal.speak())


dog = Dog()
cat = Cat()

make_sound(dog)  # "Bark!"
make_sound(cat)  # "Meow!"
Bark!
Meow!

Key Points

  • Both Dog and Cat share the same method name (speak()), but each has a distinct implementation.

  • make_sound() doesnโ€™t care whether animal is a Dog or Catโ€”it just calls animal.speak().

Polymorphism with Built-In Functions#

Pythonโ€™s built-in functions (e.g., len(), sum()) are designed to be polymorphic. You can implement magic methods (a.k.a. dunder methods) in your classes to extend these functionsโ€™ behavior to custom objects.

class CustomList:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)


my_list = CustomList([1, 2, 3, 4])
print(len(my_list))  # 4
4

Key Points

  • Different classes can implement len() to customize how len() behaves.

  • Polymorphism ensures len(obj) will work regardless of the specific class implementing it, as long as len() is provided.

Polymorphism in Inheritance Hierarchies#

Polymorphism commonly appears with inheritance, where child classes override methods defined in a parent class. The calling code doesnโ€™t need to know the childโ€™s typeโ€”just that it implements the method.

Example: A Geometry Scenario#

class Shape:
    def area(self):
        raise NotImplementedError("Must be implemented by subclasses")


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

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


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

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


shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
    print(shape.area())  # Different calculations, same .area() interface
78.53975
12

Key Points

  • Both Circle and Rectangle share the .area() method name.

  • Looping through shapes calls .area() on each object, returning distinct results based on the shape type.

Polymorphism in Everyday Code#

  1. File Handling

    • file_object.read() can be any file-like object (text file, binary file, network stream) but they all support a read() method.

  1. Collections

    • for x in collection: works for lists, sets, dictionaries, and custom iterable classes, thanks to the iter() protocol.

  1. Operator Overloading

    • a + b can mean integer addition, float addition, string concatenation, or even custom vector addition depending on the operandsโ€™ types.

Duck Typing in Python#

  • Concept: โ€œIf it walks like a duck and quacks like a duck, itโ€™s a duck.โ€

  • Python uses duck typing, meaning if an object implements the required methods or attributes, it can be treated as a โ€œduckโ€ (or any other type in question).

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


class Person:
    def quack(self):
        return "I can do a duck impression!"


def make_it_quack(entity):
    print(entity.quack())


d = Duck()
p = Person()
make_it_quack(d)  # "Quack!"
make_it_quack(p)  # "I can do a duck impression!"
Quack!
I can do a duck impression!

Why Polymorphism Matters#

  1. Code Reuse

    • Write one function (make_sound(), calculate_area()) that works for various objects.

  1. Flexibility

    • Add new classes later without modifying existing logic, as long as you respect the expected interface.

  1. Clean Architecture

    • No โ€œgiant switch statementsโ€ or type checks neededโ€”objects handle their own functionality.

Best Practices#

  1. Consistent Method Names

    • Ensure all classes that participate in a polymorphic operation use the same method name and parameters.

  1. Interfaces & Abstract Base Classes (Optional in Python)

    • Define an abstract class (using abc module) to specify required methods, ensuring all child classes provide them.

  1. Avoid Unnecessary Type Checks

    • Trust that if an object implements the method, you can call it (duck typing).

  1. Test Each Implementation

    • Verify that each class correctly fulfills the interfaceโ€™s contract (e.g., .area() returns correct geometry).

With polymorphism, your code remains elegant, extensible, and maintains a consistent interface across multiple class implementations.