Articles

Understanding Polymorphism in Python (With Examples)

Learn how to implement polymorphism in Python with practical examples and applications. Master this essential OOP concept to write more flexible, reusable code for your projects.

What is polymorphism in Python?

Polymorphism in Python refers to the ability of different objects to respond to the same method or function call in ways specific to their individual types. The term comes from Greek words “poly” (many) and “morphos” (forms), literally meaning “many forms.” Polymorphism is a core concept in object-oriented programming (OOP) that allows programmers to use a single interface with different underlying forms.

In Python, polymorphism enables you to write more flexible and reusable code by allowing objects of different classes to be treated as objects of a common superclass. This fundamental OOP principle makes your code more modular, extensible, and easier to maintain.

Let’s explore how polymorphism works in Python with a simple example:

class Bird:
def sound(self):
return "Tweet"
class Dog:
def sound(self):
return "Bark"
def make_sound(animal):
print(animal.sound())
# Create objects
bird = Bird()
dog = Dog()
# Same function call, different behaviors
make_sound(bird) # Output: Tweet
make_sound(dog) # Output: Bark

In this example, both Bird and Dog classes have a method called sound(), but each implementation returns a different result. The make_sound() function accepts any object that has a sound() method, demonstrating polymorphism in action.

Related Course

Learn Java: Inheritance and Polymorphism

Learn how to go further with classes by using inheritance and polymorphism.Try it for free

Types of polymorphism in Python

Python supports several types of polymorphism. Understanding each type will help you leverage polymorphism effectively in your code.

1. Duck typing

Duck typing is a concept related to dynamic typing where the type or class of an object is less important than the methods it defines. The name comes from the saying: “If it walks like a duck and quacks like a duck, then it probably is a duck.”

class Duck:
def swim(self):
return "Duck swimming"
def fly(self):
return "Duck flying"
class Airplane:
def fly(self):
return "Airplane flying"
def fly_test(entity):
print(entity.fly())
# Create objects
duck = Duck()
airplane = Airplane()
# Same function works with different objects
fly_test(duck) # Output: Duck flying
fly_test(airplane) # Output: Airplane flying

In this example, the fly_test() function works with any object that has a fly() method, regardless of its type. This is a typical example of duck typing.

2. Method overriding

Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its parent class. This is one of the most common forms of polymorphism in Python.

class Animal:
def speak(self):
return "Animal makes a sound"
class Cat(Animal):
def speak(self):
return "Meow"
class Cow(Animal):
def speak(self):
return "Moo"
# Create objects
animal = Animal()
cat = Cat()
cow = Cow()
# Same method name, different behaviors
print(animal.speak()) # Output: Animal makes a sound
print(cat.speak()) # Output: Meow
print(cow.speak()) # Output: Moo

In this example, both Cat and Cow classes override the speak() method of their parent class Animal.

3. Operator overloading

Operator overloading is a feature in Python that allows the same operator to have different meanings according to the context. For instance, the + operator performs arithmetic addition on numbers but concatenation on strings.

class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Point({self.x}, {self.y})"
# Create Point objects
p1 = Point(1, 2)
p2 = Point(3, 4)
# Using the + operator on Point objects
p3 = p1 + p2
print(p3) # Output: Point(4, 6)

In this example, we’ve overloaded the + operator for the Point class by implementing the __add__ special method.

4. Function and method overloading

Unlike some other OOP languages, Python doesn’t support traditional function or method overloading, where multiple methods with the same name but different parameters are defined. However, Python achieves similar functionality through default parameters and variable-length arguments.

def calculate_area(length, width=None):
if width is None:
return length * length
return length * width
# Using the function with different arguments
print(calculate_area(5)) # Output: 25 (square)
print(calculate_area(4, 6)) # Output: 24 (rectangle)

This example shows how a single function can handle different types of inputs, demonstrating polymorphic behavior.

Examples of polymorphism in Python

Let’s look at some more practical examples of polymorphism in Python to better understand how it works in real-world scenarios.

Example 1: Polymorphism with class methods

class Shape:
def area(self):
pass
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
def perimeter(self):
return 2 * 3.14 * self.radius
# Create shape objects
rectangle = Rectangle(5, 4)
circle = Circle(3)
# Process different shapes using the same methods
shapes = [rectangle, circle]
for shape in shapes:
print(f"Area: {shape.area()}")
print(f"Perimeter: {shape.perimeter()}")

In this example, both Rectangle and Circle inherit from Shape and override its methods. We can process different shapes with the same code, demonstrating polymorphism.

Example 2: Polymorphism with built-in functions

Python’s built-in functions like len() and str() also demonstrate polymorphism. They work with different types of objects:

# The len() function with different data types
print(len("Hello")) # Output: 5
print(len([1, 2, 3])) # Output: 3
print(len({"name": "John", "age": 30})) # Output: 2
# The str() function with different data types
print(str(123)) # Output: "123"
print(str([1, 2, 3])) # Output: "[1, 2, 3]"
print(str({"a": 1})) # Output: "{'a': 1}"

Here, len() and str() behave differently based on the type of object they’re called with, demonstrating polymorphic behavior.

Why use polymorphism in Python

Polymorphism offers several benefits that make it a valuable tool in Python programming:

  1. Code Reusability: Polymorphism allows you to reuse code by defining methods in a base class that subclasses can override according to their specific needs.

  2. Flexibility: It provides flexibility to use objects of different types through a common interface, making your code more adaptable to changes.

  3. Abstraction: Polymorphism helps in hiding the complex implementation details while exposing a simple interface to the users of your code.

  4. Maintainability: By promoting code organization and reusability, polymorphism makes your code easier to maintain and extend.

  5. Cleaner Code: It reduces conditional statements and type checks, resulting in cleaner and more readable code.

Practical applications of polymorphism

Polymorphism isn’t just a theoretical concept; it has numerous practical applications in real-world Python programming:

Application 1: GUI programming

In graphical user interface (GUI) programming, different UI elements (buttons, text boxes, etc.) may respond differently to the same events (clicks, keypresses):

class Button:
def click(self):
return "Button clicked"
class Checkbox:
def click(self):
return "Checkbox toggled"
def handle_click(ui_element):
print(ui_element.click())
# Create UI elements
button = Button()
checkbox = Checkbox()
# Handle clicks on different elements
handle_click(button) # Output: Button clicked
handle_click(checkbox) # Output: Checkbox toggled

Application 2: database interfaces

Different database systems can be accessed through a common interface using polymorphism:

class Database:
def connect(self):
pass
def execute_query(self, query):
pass
class MySQLDatabase(Database):
def connect(self):
return "Connected to MySQL"
def execute_query(self, query):
return f"Executing in MySQL: {query}"
class PostgreSQLDatabase(Database):
def connect(self):
return "Connected to PostgreSQL"
def execute_query(self, query):
return f"Executing in PostgreSQL: {query}"
def run_query(database, query):
print(database.connect())
print(database.execute_query(query))
# Create database objects
mysql = MySQLDatabase()
postgres = PostgreSQLDatabase()
# Run the same query on different databases
run_query(mysql, "SELECT * FROM users")
run_query(postgres, "SELECT * FROM users")

Application 3: file handling

Polymorphism can be used to handle different file types with a uniform interface:

class FileHandler:
def open(self, filename):
pass
def process(self, data):
pass
class TextFileHandler(FileHandler):
def open(self, filename):
return f"Opening text file: {filename}"
def process(self, data):
return f"Processing text data: {data}"
class ImageFileHandler(FileHandler):
def open(self, filename):
return f"Opening image file: {filename}"
def process(self, data):
return f"Processing image data: {data}"
def handle_file(handler, filename, data):
print(handler.open(filename))
print(handler.process(data))
# Create file handlers
text_handler = TextFileHandler()
image_handler = ImageFileHandler()
# Handle different file types
handle_file(text_handler, "document.txt", "Hello World")
handle_file(image_handler, "photo.jpg", "Binary data")

Conclusion

Polymorphism in Python is a powerful concept that allows for more flexible, reusable, and maintainable code. By understanding and applying the different types of polymorphism — duck typing, method overriding, operator overloading, and function overloading — you can write more elegant and efficient Python programs.

To deepen your understanding of object-oriented programming concepts like polymorphism, check out Codecademy’s Learn Python 3 course, which covers these topics in depth with interactive exercises.

Frequently Asked Questions

1. What is the difference between polymorphism and inheritance in Python?

While both are important OOP concepts, they serve different purposes. Inheritance is a mechanism where a class inherits attributes and methods from another class. It promotes code reuse and establishes a parent-child relationship between classes. Polymorphism, on the other hand, is the ability to use a common interface for different types of objects. Polymorphism often works with inheritance but can also exist independently (as in duck typing).

2. What is the role of the isinstance() function in polymorphism?

The isinstance() function can be used to check if an object is an instance of a specific class or its subclasses. While it’s sometimes useful, relying too heavily on isinstance() checks can defeat the purpose of polymorphism, which aims to treat objects based on their behavior rather than their types. Duck typing is often a more Pythonic approach.

3. Can I use polymorphism with Python’s built-in data types?

Yes, Python’s built-in types like lists, dictionaries, and strings already exhibit polymorphic behavior with operations like iteration, indexing, and methods like append(), update(), etc. You can also extend built-in types to add custom polymorphic behavior.

4. Is polymorphism only useful in large applications?

While polymorphism shows its full potential in larger applications, it can be beneficial even in smaller projects. By promoting cleaner, more organized code, polymorphism makes your code more maintainable and easier to extend, regardless of the project size.

Codecademy Team

'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'

Meet the full team