Python tutorials > Object-Oriented Programming (OOP) > Polymorphism > Examples of polymorphism?

Examples of polymorphism?

Polymorphism, meaning 'many forms,' is a fundamental concept in object-oriented programming (OOP). It allows objects of different classes to respond to the same method call in their own specific ways. This tutorial explores different types of polymorphism in Python with detailed examples.

What is Polymorphism?

Polymorphism allows you to write code that can work with objects of different types in a uniform way. It simplifies code and enhances flexibility. In essence, polymorphism is the ability of an object to take on many forms. This is achieved through method overriding and method overloading (though method overloading isn't directly supported in Python in the traditional sense).

Duck Typing (Implicit Polymorphism)

Duck typing is a form of implicit polymorphism where the type of an object is less important than the methods it defines. If it walks like a duck and quacks like a duck, then it must be a duck! In this example, both the Duck and Person classes have a quack() method. The make_it_quack() function doesn't care about the object's type; it only cares that the object has a quack() method.

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

class Person:
    def quack(self):
        return "I'm imitating a duck: Quack!"

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

duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

Explanation of Duck Typing

Python doesn't enforce strict type checking at compile time. The interpreter checks the method's existence only at runtime. This enables flexibility but requires careful design to avoid runtime errors if an object doesn't have the expected method.

Polymorphism with Inheritance

Here, the Animal class defines a speak() method. The Dog and Cat classes inherit from Animal and override the speak() method to provide their own implementations. The animal_sound() function can accept any object that is an instance of Animal (or a subclass of Animal) and call its speak() method. This is a classic example of polymorphism using inheritance.

class Animal:
    def speak(self):
        return "Generic animal sound"

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

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

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

animal = Animal()
dog = Dog()
cat = Cat()

animal_sound(animal)
animal_sound(dog)
animal_sound(cat)

Explanation of Inheritance Polymorphism

The subclasses inherit the parent class's interface (speak() method) but provide their specific implementations. This allows treating different types of objects uniformly through a common interface. The animal_sound function doesn't need to know the specific type of animal; it only knows that it can call the speak() method.

Polymorphism with Abstract Base Classes (ABCs)

Abstract Base Classes (ABCs) provide a way to define interfaces and ensure that subclasses implement specific methods. The Shape class is an abstract base class with an abstract method area(). The Circle and Square classes inherit from Shape and implement the area() method. Attempting to instantiate `Shape` will raise a `TypeError`. ABCs enforce a certain interface across different classes.

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 Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side * self.side

def print_area(shape):
    print(f"Area: {shape.area()}")

circle = Circle(5)
square = Square(4)

print_area(circle)
print_area(square)

Explanation of Abstract Base Classes

Using ABCs ensures that subclasses adhere to a specific contract (implement specific methods). This enhances code reliability and maintainability. ABCs explicitly declare what methods a class must implement. In this example if `Circle` or `Square` did not implement `area()` a `TypeError` would be raised when trying to instantiate the classes.

Real-Life Use Case: File Handling

Consider reading data from different file formats (text, CSV, JSON). Each file type requires a different reading implementation, but the fundamental operation (read()) remains the same. Polymorphism allows you to handle various file types through a common interface. The process_file() function can handle both TextFile and CSVFile objects because they both have a read() method, even though the implementation is different.

class TextFile:
    def __init__(self, filename):
        self.filename = filename

    def read(self):
        with open(self.filename, 'r') as f:
            return f.read()

class CSVFile:
    def __init__(self, filename):
        self.filename = filename

    def read(self):
        import csv
        with open(self.filename, 'r') as f:
            reader = csv.reader(f)
            return list(reader)

def process_file(file):
    data = file.read()
    print(data)

text_file = TextFile('my_text_file.txt')
csv_file = CSVFile('my_csv_file.csv')

process_file(text_file)
process_file(csv_file)

Best Practices

  • Design for Flexibility: Identify common operations that can be applied to different object types.
  • Use Inheritance Wisely: Implement inheritance when there's a clear "is-a" relationship between classes.
  • Employ Abstract Base Classes: When you need to enforce a specific interface.
  • Consider Duck Typing: Where the type is less important than the existence of a specific method.

Interview Tip

Be prepared to explain different types of polymorphism with examples (duck typing, inheritance, abstract base classes). Explain how polymorphism improves code reusability and maintainability. Highlight how it allows objects to respond to the same method call in their own ways.

When to use Polymorphism

Use polymorphism when:
  • You need to handle objects of different classes in a uniform way.
  • You want to extend functionality without modifying existing code.
  • You want to improve code reusability and maintainability.

Pros

  • Code Reusability: Write generic code that works with multiple types.
  • Flexibility: Easily add new types without modifying existing code.
  • Maintainability: Easier to maintain and modify code due to clear interfaces.

Cons

  • Complexity: Can increase the complexity of the code if not used carefully.
  • Potential Runtime Errors (Duck Typing): Errors may occur at runtime if an object doesn't have the expected method.

FAQ

  • What is the difference between duck typing and inheritance polymorphism?

    Duck typing relies on the presence of specific methods regardless of inheritance, while inheritance polymorphism relies on inheriting from a common base class and overriding methods. Duck typing is implicit, while inheritance polymorphism is explicit.
  • When should I use Abstract Base Classes (ABCs)?

    Use ABCs when you want to enforce that subclasses implement specific methods. This provides a clear contract between the base class and its subclasses.
  • Is method overloading supported in Python in the traditional sense?

    No, Python does not support method overloading in the same way as languages like Java or C++. However, you can achieve similar results using default argument values or variable-length argument lists.