Open In App

Polymorphism in Python

Last Updated : 16 Dec, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Polymorphism is a foundational concept in programming that allows entities like functions, methods or operators to behave differently based on the type of data they are handling. Derived from Greek, the term literally means “many forms”.

Python’s dynamic typing and duck typing make it inherently polymorphic. Functions, operators and even built-in objects like loops exhibit polymorphic behavior.

Polymorphism in Built-in Functions

Python’s built-in functions exhibit polymorphism, adapting to various data types.

Example:

Python
print(len("Hello"))  # String length
print(len([1, 2, 3]))  # List length

print(max(1, 3, 2))  # Maximum of integers
print(max("a", "z", "m"))  # Maximum in strings

Python determines behavior at runtime, enabling these functions to work across diverse types without explicit type declarations.

Let’s explore polymorphism in detail:

Polymorphism in Functions

Duck typing enables functions to work with any object regardless of its type.

Example:

Python
def add(a, b):
    return a + b

print(add(3, 4))           # Integer addition
print(add("Hello, ", "World!"))  # String concatenation
print(add([1, 2], [3, 4])) # List concatenation

Polymorphism in Operators

Operator Overloading

In Python, operators like + behave polymorphically, performing addition, concatenation or merging based on the data type.

Example:

Python
print(5 + 10)  # Integer addition
print("Hello " + "World!")  # String concatenation
print([1, 2] + [3, 4])  # List concatenation

Polymorphism in OOPs

In OOP, polymorphism allows methods in different classes to share the same name but perform distinct tasks. This is achieved through inheritance and interface design. Polymorphism complements other OOP principles like inheritance (sharing behavior) and encapsulation (hiding complexity) to create robust and modular applications.

Example:

Python
class Shape:
    def area(self):
        return "Undefined"

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

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

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

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

shapes = [Rectangle(2, 3), Circle(5)]
for shape in shapes:
    print(f"Area: {shape.area()}")

Explanation:

  • The code showcases polymorphism using a parent class Shape and child classes Rectangle and Circle.
  • Parent Class Shape: Contains a generic area method returning “Undefined”, acting as a placeholder for derived classes to override.
  • Child Class Rectangle: Initializes length and width via the __init__ constructor. Overrides the area method to return the rectangle’s area as length * width.
  • Child Class Circle: Initializes radius via the __init__ constructor. Overrides the area method to return the circle’s area as 3.14 * radius^2.
  • Polymorphic Behavior: A list of shape objects (Rectangle and Circle) is created. A for loop iterates through the list, calling the area method on each object. The method executed is determined by the object’s type, showcasing polymorphism.

Types of Polymorphism

Compile-time Polymorphism

  • Found in statically typed languages like Java or C++, where the behavior of a function or operator is resolved during the program’s compilation phase.
  • Examples include method overloading and operator overloading, where multiple functions or operators can share the same name but perform different tasks based on the context.
  • In Python, which is dynamically typed, compile-time polymorphism is not natively supported. Instead, Python uses techniques like dynamic typing and duck typing to achieve similar flexibility.

Runtime Polymorphism

  • Occurs when the behavior of a method is determined at runtime based on the type of the object.
  • In Python, this is achieved through method overriding: a child class can redefine a method from its parent class to provide its own specific implementation.
  • Python’s dynamic nature allows it to excel at runtime polymorphism, enabling flexible and adaptable code.

Example:

Python
class Animal:
    def sound(self):
        return "Some generic sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow"

# Polymorphic behavior
animals = [Dog(), Cat(), Animal()]
for animal in animals:
    print(animal.sound())  # Calls the overridden method based on the object type

Output
Bark
Meow
Some generic sound

Explanation: Here, the sound method behaves differently depending on whether the object is a Dog, Cat or Animal and this decision happens at runtime. This dynamic nature makes Python particularly powerful for runtime polymorphism.

Inheritance Class Polymorphism

Inheritance-based polymorphism occurs when a subclass overrides a method from its parent class, providing a specific implementation. This process of re-implementing a method in the child class is known as Method Overriding.  

Example:

Python
class Animal:
    def sound(self):
        return "Some generic animal sound"

class Dog(Animal):
    def sound(self):
        return "Bark"

class Cat(Animal):
    def sound(self):
        return "Meow

Explanation:

  • Class Animal: Acts as the base (parent) class. Contains a method sound that provides a default behavior, returning “Some generic animal sound”. This serves as a generic representation of the sound method for all animals.
  • Class Dog: Inherits from the Animal class (denoted by class Dog(Animal)). Overrides the sound method to return “Woof Woof!”, a behavior specific to dogs. This demonstrates method overriding, where the subclass modifies the implementation of the parent class’s method.
  • Class Cat: Inherits from the Animal class (denoted by class Cat(Animal)). Overrides the sound method to return “Meow”, a behavior specific to cats. Like Dog, this also demonstrates method overriding.


Next Article

Similar Reads