Skip to main content

Python Polymorphism

Introduction to Polymorphism

Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common superclass. The word "polymorphism" comes from Greek, meaning "many forms." In programming terms, it refers to the ability of different objects to respond to the same method call in ways specific to their individual classes.

In Python, polymorphism enables you to write more flexible and reusable code by allowing:

  • Objects of different types to be handled using a common interface
  • Methods with the same name to behave differently depending on the object they're called on
  • Code that works with a parent class to also work with all child classes

By the end of this tutorial, you'll understand how polymorphism works in Python and how to implement it in your own projects.

Basic Principles of Polymorphism in Python

Python's dynamic typing naturally supports polymorphism. Unlike statically typed languages that might require explicit interfaces, Python follows the principle of "duck typing" - if an object walks like a duck and quacks like a duck, it can be treated as a duck.

Let's look at a simple example of polymorphism with built-in Python functions:

python
# Different types responding to the same function
print(len("Hello")) # String
print(len([1, 2, 3])) # List
print(len({"a": 1, "b": 2})) # Dictionary

Output:

5
3
2

In this example, the len() function works with different data types. Even though a string, a list, and a dictionary are completely different objects internally, they all respond to the same function call.

Polymorphism in Class Methods

One of the most common ways to implement polymorphism in Python is through class inheritance and method overriding.

Method Overriding

Let's create an example with different animal classes that implement the same method differently:

python
class Animal:
def speak(self):
pass

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

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

class Duck(Animal):
def speak(self):
return "Quack!"

Now we can create a function that handles any Animal object:

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

# Create objects
dog = Dog()
cat = Cat()
duck = Duck()

# Call the function with different objects
animal_sound(dog)
animal_sound(cat)
animal_sound(duck)

Output:

Woof!
Meow!
Quack!

This is polymorphism in action! The animal_sound function doesn't need to know which specific animal it's dealing with. It only needs to know that the object has a speak() method.

Polymorphism with Method Overloading (Sort of)

Python doesn't support traditional method overloading (having multiple methods with the same name but different parameters). However, you can simulate it using default parameters or variable arguments:

python
class Calculator:
def add(self, *args):
result = 0
for num in args:
result += num
return result

# Create calculator object
calc = Calculator()

# Call the same method with different numbers of arguments
print(calc.add(2, 3))
print(calc.add(5, 10, 15))
print(calc.add(1, 2, 3, 4, 5))

Output:

5
30
15

Polymorphism Through Duck Typing

Python's "duck typing" is a concept where the type or class of an object is less important than the methods it defines or the operations it supports. This is a form of polymorphism that doesn't rely on inheritance:

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

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

class Human:
def speak(self):
return "Hello!"

def make_speak(entity):
print(entity.speak())

# These don't inherit from a common base class
dog = Dog()
cat = Cat()
human = Human()

# Yet they can all be used with the same function
make_speak(dog)
make_speak(cat)
make_speak(human)

Output:

Woof!
Meow!
Hello!

Real-World Example: File Operations

Let's see how polymorphism works in a more practical scenario with file operations:

python
class FileHandler:
def open_file(self):
pass

def process_data(self):
pass

def close_file(self):
pass

def execute(self):
self.open_file()
self.process_data()
self.close_file()

class TextFileHandler(FileHandler):
def __init__(self, filename):
self.filename = filename
self.contents = None

def open_file(self):
print(f"Opening text file: {self.filename}")
self.contents = "Sample text content"

def process_data(self):
print(f"Processing text data: {self.contents}")
# Processing logic for text

def close_file(self):
print(f"Closing text file: {self.filename}")
self.contents = None

class ImageFileHandler(FileHandler):
def __init__(self, filename):
self.filename = filename
self.image_data = None

def open_file(self):
print(f"Opening image file: {self.filename}")
self.image_data = "Binary image data"

def process_data(self):
print(f"Processing image data with specialized algorithm")
# Image processing logic

def close_file(self):
print(f"Closing image file: {self.filename}")
self.image_data = None

# Client code
def process_file(file_handler):
file_handler.execute()

# Process different file types
text_handler = TextFileHandler("document.txt")
image_handler = ImageFileHandler("photo.jpg")

process_file(text_handler)
print("\n---\n")
process_file(image_handler)

Output:

Opening text file: document.txt
Processing text data: Sample text content
Closing text file: document.txt

---

Opening image file: photo.jpg
Processing image data with specialized algorithm
Closing image file: photo.jpg

In this example, both file handlers share the same interface provided by the FileHandler class, but they implement the methods differently according to their specific file type requirements.

Abstract Base Classes and Polymorphism

Python provides the abc (Abstract Base Classes) module, which can be used to enforce certain methods to be implemented by subclasses, making polymorphism more structured:

python
from abc import ABC, abstractmethod

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

@abstractmethod
def perimeter(self):
pass

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

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

def perimeter(self):
return 2 * (self.width + self.height)

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

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

def perimeter(self):
return 2 * 3.14 * self.radius

# Function that works with any Shape object
def print_shape_info(shape):
print(f"Area: {shape.area()}")
print(f"Perimeter: {shape.perimeter()}")

# Create shape objects
rectangle = Rectangle(5, 4)
circle = Circle(3)

# Process different shapes
print("Rectangle:")
print_shape_info(rectangle)

print("\nCircle:")
print_shape_info(circle)

Output:

Rectangle:
Area: 20
Perimeter: 18

Circle:
Area: 28.26
Perimeter: 18.84

Using ABCs ensures that any class inheriting from Shape will be required to implement the specified methods, making the code more robust.

Benefits of Polymorphism

Polymorphism brings several advantages to your Python code:

  1. Code Reusability: Write functions that can work with objects of multiple types
  2. Extensibility: Add new classes that work with existing code without modifying it
  3. Flexibility: Create more generic and adaptable interfaces
  4. Cleaner Code: Reduces conditional statements that check for object types
  5. Maintainability: Makes code easier to understand and update

Summary

Polymorphism is a powerful concept in Python's object-oriented programming that allows objects of different classes to be treated through a common interface. We've explored:

  • Basic principles of polymorphism in Python
  • Method overriding as a form of polymorphism
  • Duck typing as Python's approach to polymorphism
  • Practical applications with real-world examples
  • How abstract base classes can enforce polymorphic behavior

By mastering polymorphism, you can write more flexible, maintainable, and elegant Python code that's both powerful and easy to extend.

Exercises

  1. Create a Payment class hierarchy with subclasses like CreditCardPayment, PayPalPayment, and BankTransferPayment that each implement a process() method differently.

  2. Implement a shape hierarchy with different shapes (Triangle, Square, etc.) that share a common draw() method but render differently.

  3. Write a function that accepts any object with a to_json() method and processes the resulting JSON data.

  4. Create a simple media player that can play different types of media (audio, video) using polymorphism.

Further Reading

By understanding and applying polymorphism in your Python programs, you'll create more flexible and maintainable code that can adapt to new requirements without major refactoring.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)