Nearby lessons
108 of 159Python - Polymorphism
- Understand the concept of polymorphism with real-life examples
- Explain dynamic typing and the duck typing philosophy of Python
- Handle missing methods safely using hasattr()
- Understand why Python does not support traditional method overloading
- Implement method overloading using default arguments and *args
What is Polymorphism?
Poly means Many.
Morphs means Forms.
Therefore, Polymorphism means "Many Forms".
In Object-Oriented Programming (OOP), polymorphism allows the same object, operator, or method to perform different tasks depending on the situation.
Python supports polymorphism through operators, methods, inheritance, and dynamic typing.
Real-Life Example 1: Human Behaviour
Yourself is the best example of polymorphism.
- In front of your parents, you have one type of behaviour.
- With your friends, you have another type of behaviour.
Although the person is the same, the behaviour changes depending on the situation. This is called Polymorphism.
Real-Life Example 2: The + Operator
The + operator performs multiple purposes:
- Arithmetic Addition
- String Concatenation
Real-Life Example 3: The * Operator
The * operator also performs multiple purposes:
- Multiplication
- Repetition Operator
Topics Related to Polymorphism
The following topics are important in Polymorphism:
- Duck Typing Philosophy of Python
- Overloading
- Operator Overloading
- Method Overloading
- Constructor Overloading
- Overriding
- Method Overriding
- Constructor Overriding
Operator overloading is covered on its own page, while overriding is covered in the inheritance tutorial.
Different Examples of Polymorphism
| Example | Polymorphic Behaviour |
|---|---|
| Human Behaviour | Same person behaves differently in different situations. |
| + Operator | Addition and String Concatenation. |
| * Operator | Multiplication and String Repetition. |
| Method Overriding | Different implementations of the same method. |
Dynamic Typing
In Python, we cannot specify the type of an object explicitly.
Based on the value provided at runtime, Python automatically determines the object's type.
Because of this behaviour, Python is called a Dynamically Typed Programming Language.
Unlike statically typed languages, Python does not require programmers to declare variable types before using them. The type checking is performed during program execution.
Duck Typing Principle
Python follows the famous Duck Typing Principle:
If it walks like a duck and talks like a duck, it must be a duck.
Python is not interested in the actual type of an object.
Instead, Python checks whether the object provides the required behaviour (methods or attributes).
If the object supports the required method, Python allows the operation. This principle is called the Duck Typing Philosophy of Python.
How Duck Typing Works
Suppose different objects provide a talk() method:
| Object Passed | Method Called | Output |
|---|---|---|
| Duck() | talk() | Quack. Quack. |
| Cat() | talk() | Moew Moew. |
| Dog() | talk() | Bow Bow. |
| Goat() | talk() | Myaah Myaah. |
Notice that all objects are different, but they all provide the talk() method. Hence, the same function works successfully with every object.
Problem with Duck Typing
The main problem with the Duck Typing approach is that Python assumes the object contains the required method.
If the object does not contain that method, the program terminates with an AttributeError.
Python checks an object's behaviour only when the method is called. If the required behaviour is missing, an exception is raised.
What is AttributeError?
AttributeError is a built-in Python exception.
It occurs when we try to access a method or variable that does not exist inside an object.
In the following example, the function expects every object to provide a talk() method. If an object does not have that method, Python raises an AttributeError.
AttributeError:
'Dog' object has no attribute 'talk'
This is the main drawback of Duck Typing if proper checking is not performed before calling a method.
Solution: The hasattr() Function
The problem with Duck Typing can be solved by using the hasattr() function.
Instead of directly calling a method, we first check whether the required method exists inside the object.
- If the method is available, it is executed.
- If the method is not available, Python checks for another suitable method.
This approach prevents the program from generating an AttributeError.
Demo Program Using hasattr()
How hasattr() Works
| Object | talk() | bark() | Method Executed |
|---|---|---|---|
| Duck() | Yes | No | talk() |
| Human() | Yes | No | talk() |
| Dog() | No | Yes | bark() |
The program safely selects the appropriate method based on the available attributes.
Advantages of hasattr()
- Prevents AttributeError.
- Checks whether an attribute exists before using it.
- Works with both methods and variables.
- Makes Duck Typing programs safer.
- Improves program reliability.
What is Overloading?
Overloading means providing multiple behaviours for the same operator or method.
In different situations, the same operator or method performs different tasks.
Python supports Operator Overloading directly.
However, Python does not support Method Overloading and Constructor Overloading in the traditional sense as found in languages like Java or C++.
These concepts are achieved in Python by using default arguments or variable-length arguments.
Example: Operator Overloading
The + operator performs multiple operations depending on the operands.
Example: The * Operator
The * operator is another example of operator overloading. It performs multiplication as well as repetition.
Types of Overloading
| Type | Description |
|---|---|
| Operator Overloading | Same operator performs different operations. |
| Method Overloading | Same method with different parameter lists (not directly supported in Python). |
| Constructor Overloading | Same constructor with different parameter lists (implemented using default or variable arguments). |
For a deep dive into operator overloading with magic methods, visit the operator overloading tutorial. Constructor overloading is covered in the constructor tutorial.
What is Method Overloading?
If two methods have the same name but different types or numbers of arguments, then the concept is called Method Overloading.
For example, we may want a method named m1() to work in different ways:
Why Traditional Method Overloading is Not Supported
In programming languages such as Java and C++, we can define multiple methods with the same name but different parameter lists.
Python keeps a method name as an attribute of the class. When another method with the same name is defined, the new definition replaces the earlier one:
First m1()
↓
Replaced by
Second m1(a)
↓
Replaced by
Third m1(a, b)
Only the final definition remains available through the class. Hence, traditional method overloading based only on different parameter lists is not supported directly in Python.
How Can We Handle Method Overloading in Python?
Even though traditional Method Overloading is not supported directly, we can implement similar behaviour using:
- Default Arguments
- Variable Length Arguments
Method Overloading with Default Arguments
We can use default values for parameters so that the same method can be called with different numbers of arguments.
Explanation
Only one sum() method is defined, with all parameters defaulting to None.
t.sum(10, 20): the second condition is true, so it printsThe Sum: 30.t.sum(10, 20, 30): the first condition is true, so it printsThe Sum: 60.t.sum(10): neither condition is satisfied, so it printsPlease provide 2 or 3 arguments.
Method Overloading with Variable Length Arguments
Another approach is to use Variable Length Arguments.
Python uses *args to accept any number of positional arguments.
Explanation
The *a parameter receives any number of positional arguments as a tuple.
Every argument is added to total inside the loop:
t.sum(): the tuple is empty, so the total is 0.t.sum(10): total is 10.t.sum(10, 20): total is 30.t.sum(10, 20, 30): total is 60.t.sum(10, 20, 30, 40): total is 100.
Thus, a single method can process different numbers of arguments.
Default Arguments vs Variable Length Arguments
| Feature | Default Arguments | Variable Length Arguments |
|---|---|---|
| Syntax | a=None |
*args |
| Number of Arguments | Usually limited by declared parameters | Any number of positional arguments |
| Values Received As | Individual parameters | Tuple |
| Flexibility | Moderate | High |
| Useful When | Known optional parameters | Number of arguments can vary widely |
Method Overloading vs Method Overriding
| Feature | Method Overloading | Method Overriding |
|---|---|---|
| Basic Idea | Same method name with different argument patterns | Child class redefines an inherited method |
| Inheritance Required | No | Yes |
| Traditional Direct Support in Python | No | Yes |
| Classes Involved | Usually one class conceptually | Parent and Child classes |
| Main Purpose | Handle different arguments | Change inherited behaviour |
| Parent Implementation Access | Not applicable | Can use super() |
- Polymorphism means many forms: the same object, operator, or method behaves differently by situation
- Python is dynamically typed because variable types are decided at runtime
- Duck typing checks behaviour (methods) instead of the actual type of an object
- hasattr() prevents AttributeError by checking for a method before calling it
- Traditional method overloading is not supported; default arguments and *args achieve the same result