Learn Polymorphism in Python by Example
Polymorphism means "One with multiple forms". It is one of the key features of object-oriented programming(OOP). It empowers developers to write flexible and adaptable code allowing different objects to respond to the same function or method in distinct ways. Therefore, promoting code reusability and modularity. This article will walk through the concept of Polymorphism in Python. You will understand its significance in OOP, polymorphic functions, method overloading and method overriding, and how inheritance and interfaces enable polymorphic behaviour. Through practical examples, you will witness the true power and versatility of polymorphism.
Understanding the Polymorphism
Imagine you are visiting a grocery store to buy several grocery items. The shop has a shopkeeper who finds and gives the requested item(s). Each time you request him for some item, he will bring the item and hand it over to you.
Here, you need to pay attention to that shop is a complete application and the shopkeeper is the single function that serves you to get the items you want based on what data you pass to the shopkeeper()
function. You can not deny the below possibilities:
1. Asking the shopkeeper for each item one by one. See the below sample code.
inventory = [
'apple',
'oranges',
'pineapple',
'avocado',
'grapes',
'mango',
...
]
def shopkeeper(item):
if item in inventory:
return item
return f'{item} - Not available'
print(shopkeeper('grapes'))
print(shopkeeper('potatos'))
# Output
# grapes
# potatos - Not available
2. Ask the shopkeeper to give all items at once. See the code below.
inventory = [
'apple',
'oranges',
'pineapple',
'avocado',
'grapes',
'mango',
...
]
def shopkeeper(items):
result = []
for item in items:
if item in inventory:
result.append(item)
else:
result.append(f'{item} - Not available')
return result
print(shopkeeper(['grapes', 'potatos', 'oranges']))
As you can understand the shopkeeper assumes the role of multiple nature of work. He can facilitate a single item as well as multiple items based on the request. This is the concept of polymorphism where one function/object changes its behaviour based on the nature of work.
Significance of Polymorphism in OOP
You might know the trigonometry terminology to denote multi-angle shapes. We have different words such as Triangle, Rectangle, Pentagon, Hexagon etc. To denote all these at once we commonly use "Polygon" meaning the shape has many angles. Below is the Google screenshot for reference of where this "polygon" word came from.
The "Polymorphism" word also comes from the Greek words "poly," meaning "many," and "morph," meaning "form". Therefore, we can understand the word "Polymorphic" as "The one who has many shapes" and "Polymorphism" as the "ability to take multiple forms".
So, do you think the shopkeeper() function is polymorphic? If you differ, please comment.
In our shopkeeper example, you must have seen that two functions operate on different data types that are string and list.
In the Object Oriented Programming context, polymorphism allows a single function or method to operate on different data types or classes seamlessly. It weaponizes developers to write code that can handle diverse data without knowing the specific types at compile time. Let's combine both examples for different possibilities below:
inventory = [
'apple',
'oranges',
'pineapple',
'avocado',
'grapes',
'mango',
...
]
def shopkeeper(item):
if isinstance(item, str): # Check if the shopkeeper function is given "string" parameter
if item in inventory:
return item
else:
return f'{item} - Not available'
elif isinstance(item, list): # Check if the shopkeeper function is given "list" parameter
result = []
for i in item:
if i in inventory:
result.append(i)
else:
result.append(f'{i} - Not available')
return result
else:
raise TypeError("Invalid input type")
# Now you can pass either single item or all items to shopkeeper()
print("Ask shopkeeper to give items one by one")
print(shopkeeper('grapes'))
print(shopkeeper('potatos'))
print("Ask shopkeeper to give all items at once")
print(shopkeeper(['grapes', 'potatos', 'oranges']))
Polymorphism promotes the concept of reusability and Do not Repeat Yourself(DRY), making it tremendously easier to maintain and extend applications. Developers can create more flexible and robust software solutions by designing classes and functions with polymorphic capabilities.
Polymorphic Functions
Method Overloading
shopkeeper()
is a polymorphic function. Let's take another example so that it becomes more clear to you. Consider a simple addition function:
def add(a, b):
return a + b
This add function can handle various data types, such as integers, floats, and concatenate strings.
print(add(2, 3)) # Output: 5
print(add(3.14, 2.86)) # Output: 6.0
print(add("Hello, ", "World!")) # Output: "Hello, World!"
Method overloading is another way to achieve polymorphism within classes. It allows a class to have multiple methods with the same name but different parameters. Being a dynamic typing language, Python does not natively support method overloading, but we can achieve it through default parameter values or using the *args
and **kwargs
techniques.
See the below example that demonstrates that the second method overloads the previous method definition even if we pass different data types.
class Calculator:
def add(self, a:int, b:int):
print("I am adding numeric values")
return a + b
def add(self, a:str, b:str):
print("I am concatanating string values")
return a + b
calc = Calculator()
print(calc.add(2, 3))
print(calc.add("Smiaansh", "Technologies"))
Method Overriding
Polymorphism becomes potent when combined with inheritance. Subclasses can override methods from their superclass(s), providing their implementation while maintaining the same method name. It allows different classes to exhibit polymorphic behaviour while adhering to a standard interface.
class Shape:
def area(self):
pass # Abstract method
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
In this example, Shape
is the superclass, while Rectangle
and Circle
are subclasses. Both subclasses override the area()
method to calculate their specific area formulas.
Comparison Table
Feature | Method Overloading | Method Overriding |
---|---|---|
Definition | Same method name, different parameters | Subclass overrides superclass method |
Purpose | Handle varied inputs | Customize functionality |
Python Native Support | No | Yes |
Practical Examples
Polymorphism is evident in many built-in Python functions, such as len()
, str()
, and sum()
. These functions can work with various data types, adjusting their behaviour accordingly.
print(len([1, 2, 3])) # Output: 3
print(len("Hello, World!")) # Output: 13
print(str(123)) # Output: "123"
print(str(3.14)) # Output: "3.14"
print(sum([1, 2, 3])) # Output: 6
print(sum((1, 2, 3))) # Output: 6
Common Mistakes
Here’s a detailed breakdown of common mistakes developers make when working with polymorphism in Python, along with explanations and tips to avoid them:
1. Using Polymorphism Without Understanding Input Types
-
Mistake: Developers often assume that the input types are consistent without explicitly checking. This can lead to errors if the function receives unexpected data.
-
Example:
Code Exampledef process(item): return item.upper() # Assumes the item is a string print(process(["hello"])) # Fails with AttributeError
-
How to Avoid:
-
Use functions like
isinstance()
to handle different types or raise appropriate errors. -
Example Fix:
Code Exampledef process(item): if isinstance(item, str): return item.upper() else: raise TypeError("Expected a string")
-
2. Overcomplicating Polymorphic Logic
-
Mistake: Writing overly complex polymorphic methods that mix too many responsibilities, making them hard to maintain.
-
Example:
Code Exampledef process(item): if isinstance(item, int): return item ** 2 elif isinstance(item, str): return item[::-1] elif isinstance(item, list): return sum(item)
-
How to Avoid:
-
Stick to the Single Responsibility Principle and split such logic into multiple functions or methods. Keep each function focused on one task.
-
3. Forgetting to Override Methods Properly
-
Mistake: Subclasses may unintentionally fail to override methods due to typos or method signature mismatches.
-
Example:
Code Exampleclass Parent: def greet(self): print("Hello!") class Child(Parent): def greeting(self): # Typo: should be greet print("Hi!") obj = Child() obj.greet() # Calls Parent's method instead of Child's
-
How to Avoid:
-
Double-check overridden method names and parameters.
-
Use abstract base classes (
ABC
module) to enforce method overriding where necessary.
-
4. Overriding Without Calling super()
-
Mistake: When overriding a method in a subclass, failing to call the parent class's method can lead to loss of functionality.
-
Example:
Code Exampleclass Animal: def sound(self): print("Animal makes a sound") class Dog(Animal): def sound(self): print("Dog barks") # Doesn't call the parent method
-
How to Avoid:
-
Use
super()
to include the parent class behavior:Code Exampleclass Dog(Animal): def sound(self): super().sound() print("Dog barks")
-
5. Misusing Method Overloading
-
Mistake: Python doesn’t natively support method overloading (like Java or C++), but developers may try to use it incorrectly.
-
Example:
Code Exampleclass Calculator: def add(self, a, b): return a + b def add(self, a, b, c): # Overwrites the previous method return a + b + c calc = Calculator() print(calc.add(2, 3)) # Error: add() only supports three arguments now
-
How to Avoid:
-
Use default arguments or
*args
for flexibility. -
Example Fix:
Code Exampleclass Calculator: def add(self, *args): return sum(args)
-
6. Confusing Method Overriding and Overloading
-
Mistake: Treating method overriding as method overloading or vice versa, leading to incorrect implementation.
-
Explanation:
-
Overriding occurs when a subclass redefines a method from its parent class.
-
Overloading allows the same method name with different parameters (not natively supported in Python).
-
-
Tip: Be clear about the differences and apply them appropriately.
7. Ignoring DRY (Don't Repeat Yourself) Principle
-
Mistake: Rewriting similar methods instead of leveraging polymorphism through inheritance or interfaces.
-
Example:
Code Exampleclass Circle: def area(self, radius): return 3.14 * radius ** 2 class Square: def area(self, side): return side * side
-
How to Avoid:
-
Use a polymorphic base class:
Code Exampleclass Shape: def area(self): pass # Abstract method class Circle(Shape): def area(self, radius): return 3.14 * radius ** 2 class Square(Shape): def area(self, side): return side * side
-
8. Overlooking Code Readability
-
Mistake: Writing overly compact polymorphic code that sacrifices readability for cleverness.
-
Example:
Code Exampledef process(data): return [i ** 2 if isinstance(i, int) else i[::-1] for i in data]
-
How to Avoid:
-
Break down complex logic into smaller, readable functions or use comments to explain the logic.
-
9. Ignoring Testing for Polymorphic Scenarios
-
Mistake: Developers often test individual methods but overlook testing their polymorphic behavior with various inputs.
-
How to Avoid:
-
Create unit tests for all possible scenarios:
Code Exampledef test_shopkeeper(): assert shopkeeper("grapes") == "grapes" assert shopkeeper(["apple", "potato"]) == ["apple", "potato - Not available"]
-
10. Not Leveraging Built-In Functions
-
Mistake: Rewriting functions that Python already provides polymorphically, like
len()
,sum()
, etc. -
How to Avoid:
-
Familiarize yourself with Python’s built-in polymorphic functions and reuse them wherever possible.
-
By addressing these common mistakes, you can write cleaner, more efficient, and error-free polymorphic code in Python. Let me know if you’d like deeper examples for any of these!
Conclusion
Polymorphism is a powerful concept in Python that enhances the flexibility and adaptability of object-oriented programs. Polymorphism promotes code reuse and modularity by allowing functions and methods to operate on different data types and classes. Python empowers developers to write versatile and scalable applications leveraging polimorphism along with function or method overloading & overriding through inheritance. Embrace the essence of polymorphism and elevate your Python programming to new heights of efficiency and elegance.
Reader Comments
Add a Comment
Recent Posts
- How To Use Modules and Packages in Python?
- How To Do File Handling In Python?
- How To Handle Exceptions In Python?
- How To Manipulate String in Python?
- How To Use Regular Expression In Python?
- How To Write Object-Oriented Programs (OOP) in Python?
- How To Create Classes and Objects In Python?
- How To Use Python Dictionaries?
- How To Inherit A Class In Python?
- How To Use Encapsulation in Python?
- How To Do GUI Programming In Python?
- How To Create Caching In Django?
- Exploring the Fascinating World of Generative AI
- Hidden Facts of Python Programming
- The Art of Prompt Engineering | Crafting Engaging Content in a Snap
- Unveiling the Art of Engagement | The Power Of Prompt Engineering
- Understanding Your Audience and Goals | The Power Of Prompt Engineering
- A Deep Dive Into Crafting Compelling Prompts | The Power Of Prompt Engineering
- Psychology of Words | The Power Of Prompt Engineering
- How to define functions in python?