Table of Contents | |
What is Polymorphism in Python?
Python polymorphism means “many forms”. In simple terms, it lets you use the same function or operator on different types of objects. For example, the len()
function can find the length of a string like “hello” (which is 5) or the number of items in a list like [1, 2, 3]
(which is 3). It performs differently depending on whether it receives a string or a list, but you use the same function name, len()
. This makes your code more flexible and reusable.
Example of Python Polymorphism
def add(x, y):
return x + y
print(add(5, 3)) # Output: 8
print(add(5.5, 2.5)) # Output: 8.0
print(add("Hello", " World")) # Output: Hello World
Explanation
- The
add
Function:- We define a function called
def add(x, y):
. This function takes two inputs,x
andy
. - Inside the function, we have
return x + y
. This means the function will return the result of using the+
operator onx
andy
.
- We define a function called
- Polymorphism:
- Adding Numbers:
- In the line
print(add(5, 3))
, we call theadd
function with two integers,5
and3
. - The
+
operator, in this case, performs addition. So,5 + 3
equals8
, and that’s what gets printed. - Similarly, in
print(add(5.5, 2.5))
we are adding two floating point numbers resulting in8.0
.
- In the line
- Concatenating Strings:
- In the line
print(add("Hello", " World"))
, we call theadd
function with two strings,"Hello"
and" World"
. - Here, the same
+
operator now performs string concatenation. It joins the two strings together. - So,
"Hello" + " World"
becomes"Hello World"
, and that’s what gets printed.
- In the line
- Adding Numbers:
The add
function and the +
operator are showing polymorphism. They behave differently depending on the type of data you give them. With numbers, +
adds. With strings, +
concatenates.
Function Polymorphism in Python
Function polymorphism refers to functions that can operate on different types of data. Built-in functions like len()
are great examples of this because they can get the length of different types of sequences, such as lists, tuples, strings, or dictionaries. The function works differently depending on the object type passed.
Syntax
length = len(object)
Explanation
length = ...
: The result of thelen()
function will be stored in a variable calledlength
.len(object)
: The built-inlen()
function is called withobject
as its argument. The behavior oflen()
varies depending on the type ofobject
.
Example
my_list = [1, 2, 3, 4]
my_string = "Hello"
my_tuple = (5, 6, 7)
print(len(my_list)) # Output: 4
print(len(my_string)) # Output: 5
print(len(my_tuple)) # Output: 3
Explanation
my_list = [1, 2, 3, 4]
: A list namedmy_list
is created.my_string = "Hello"
: A string namedmy_string
is created.my_tuple = (5, 6, 7)
: A tuple namedmy_tuple
is created.print(len(my_list))
: Thelen()
function calculates the length ofmy_list
(which is 4).print(len(my_string))
: Thelen()
function calculates the length ofmy_string
(which is 5).print(len(my_tuple))
: Thelen()
function calculates the length ofmy_tuple
(which is 3).
Class Polymorphism in Python
Class polymorphism is when different classes have methods with the same name. This allows you to call the same method on objects of different classes, and each object will respond appropriately based on its class definition. Python polymorphism with class methods enables you to write more generic code that can handle objects of various classes without needing separate logic for each class type.
Example
class Animal:
def speak(self):
print("Generic animal sound")
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
animals = [Animal(), Dog(), Cat()]
for animal in animals:
animal.speak()
Explanation
class Animal:
: Defines a base classAnimal
.def speak(self):
: Defines a methodspeak
in theAnimal
class.print("Generic animal sound")
: The base class’sspeak
method prints a generic sound.class Dog(Animal):
andclass Cat(Animal):
: Define two classes,Dog
andCat
, that inherit fromAnimal
.def speak(self):
: Each derived class overrides thespeak
method.print("Woof!")
andprint("Meow!")
: TheDog
andCat
classes print their specific sounds.animals = [Animal(), Dog(), Cat()]
: Creates a list containing instances of all three classes.for animal in animals:
: Loops through the list of animals.animal.speak()
: Calls thespeak
method on each animal. Because of polymorphism, the correct version ofspeak
is called for each object.
Polymorphism and Inheritance (Method Overriding)
Method overriding is a key feature in object-oriented programming, and it’s a crucial aspect of polymorphism in Python. It allows a subclass to provide a specific implementation of a method already defined in its superclass. When a method in a subclass has the same name, same parameters, and same return type as a method in its superclass, then the subclass’s method overrides the superclass’s method. Python polymorphism, through method overriding, enables you to customize the behavior of inherited methods to fit the specific needs of the subclass.
Syntax
class ParentClass:
method my_method():
# Parent class implementation
class ChildClass(ParentClass):
method my_method():
# Child class implementation (overrides the parent's method)
Explanation
class ParentClass:
: This defines the parent class.method my_method():
: This is a method defined in theParentClass
.class ChildClass(ParentClass):
: This defines the child class, which inherits fromParentClass
.method my_method():
: This is a method withinChildClass
that has the same name as the method inParentClass
. The child’s method will override the parent’s method when called on aChildClass
object.
Example
class Animal:
def make_sound(self):
print("Generic animal sound")
class Dog(Animal):
def make_sound(self):
print("Woof!")
animal = Animal()
dog = Dog()
animal.make_sound() # Output: Generic animal sound
dog.make_sound() # Output: Woof!
Explanation
class Animal:
: Defines a base class calledAnimal
.def make_sound(self):
: Defines a methodmake_sound
in theAnimal
class.print("Generic animal sound")
: This is the default behavior ofmake_sound
in the base class.class Dog(Animal):
: Defines a subclassDog
that inherits fromAnimal
.def make_sound(self):
: This method inDog
overrides themake_sound
method ofAnimal
.print("Woof!")
: TheDog
class provides its own implementation ofmake_sound
.animal = Animal()
anddog = Dog()
: Creates instances ofAnimal
andDog
.animal.make_sound()
: Callsmake_sound
on theAnimal
object, printing the generic sound.dog.make_sound()
: Callsmake_sound
on theDog
object. Due to method overriding, theDog
‘s specific sound (“Woof!”) is printed.
Polymorphism in Python through Operator Overloading
Operator overloading is a technique that exemplifies polymorphism in Python. It allows you to define how operators behave with objects of your classes. For example, you can specify what happens when you use the +
operator to add two custom class objects. This customization of operator behavior for different classes is an important aspect of Python polymorphism.
Polymorphism in +
operator
The +
operator provides a great example of polymorphism through operator overloading. When used with numbers, +
performs addition. With strings, it performs concatenation. You can also define how the +
operator works with objects of your classes by implementing the __add__
method. This ability of the +
operator to adapt its behavior based on the operands is a clear demonstration of Python polymorphism.
Syntax
class MyClass:
method __add__(self, other):
# Define how '+' works for objects of MyClass
Explanation
class MyClass:
: Defines a class where you want to customize the behavior of the+
operator.method __add__(self, other):
: This is a special method that gets called when the+
operator is used with an object ofMyClass
on the left-hand side. It takes the object on the right-hand side of the+
operator as theother
argument.
Example
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag
def __add__(self, other):
return ComplexNumber(self.real + other.real, self.imag + other.imag)
def __str__(self):
return f"{self.real} + {self.imag}i"
c1 = ComplexNumber(2, 3)
c2 = ComplexNumber(1, 4)
c3 = c1 + c2 # Uses the overloaded '+' operator
print(c3) # Output: 3 + 7i
Explanation
class ComplexNumber:
: Defines a class to represent complex numbers.def __init__(self, real, imag):
: The constructor initializes the real and imaginary parts.self.real = real
andself.imag = imag
: Assigns the real and imaginary parts to the object.def __add__(self, other):
: This method overloads the+
operator forComplexNumber
objects.return ComplexNumber(self.real + other.real, self.imag + other.imag)
: Defines that adding twoComplexNumber
objects returns a newComplexNumber
whose real and imaginary parts are the sums of the corresponding parts of the operands.def __str__(self):
: Defines how aComplexNumber
should be represented as a string.return f"{self.real} + {self.imag}i"
: Returns a string in the format “real + imaginaryi”.c1 = ComplexNumber(2, 3)
andc2 = ComplexNumber(1, 4)
: Creates twoComplexNumber
objects.c3 = c1 + c2
: Addsc1
andc2
using the overloaded+
operator.print(c3)
: Prints the resultingComplexNumber
using the overloaded__str__
method.
Polymorphism in * operator
The *
operator also shows polymorphism. With numbers, it performs multiplication. With a string and an integer, it performs string repetition. You can even define how *
works with your classes by implementing the __mul__
method. This adaptability of the *
operator based on the context of its use is a core aspect of Python polymorphism.
Syntax
class MyClass:
method __mul__(self, other):
# Define how '*' works for objects of MyClass
Explanation
class MyClass:
: Defines a class where you intend to customize the behavior of the*
operator.method __mul__(self, other):
: This special method is invoked when the*
operator is used with an object ofMyClass
on the left-hand side. The object on the right-hand side of*
is passed as theother
argument.
Example
class StringMultiplier:
def __init__(self, string):
self.string = string
def __mul__(self, times):
return self.string * times
def __str__(self):
return self.string
s = StringMultiplier("Hello")
result = s * 3 # Uses the overloaded '*' operator
print(result) # Output: HelloHelloHello
Explanation
class StringMultiplier:
: Defines a class that will handle string multiplication.def __init__(self, string):
: The constructor initializes the string.self.string = string
: Assigns the given string to the object.def __mul__(self, times):
: This method overloads the*
operator.return self.string * times
: Defines that multiplying aStringMultiplier
object by an integer will repeat the string that many times.def __str__(self):
: Defines how aStringMultiplier
object should be represented as a string.return self.string
: Returns the stored string.s = StringMultiplier("Hello")
: Creates aStringMultiplier
object with the string “Hello”.result = s * 3
: Multiplies theStringMultiplier
object by 3, using the overloaded*
operator.print(result)
: Prints the result, which is the string repeated three times.
Disadvantages of Inheritance and Polymorphism
- Tight Coupling: Inheritance can create strong dependencies between parent and child classes.
- Base Class Problem: Changes in a base class can unintentionally break derived classes.
- Complex Hierarchies: Overuse of inheritance can lead to complicated and hard-to-manage class structures.
- Code Predictability: Polymorphism can make it harder to predict the exact behavior of code during runtime.
- Increased Debugging Effort: Debugging can be more challenging due to the dynamic nature of polymorphism.
- Runtime Errors: Polymorphism’s type checking at runtime can lead to errors not caught during development.
Conclusion
Python polymorphism helps write better code. It lets you use the same function or operator differently, depending on the object you’re working with. This makes code more flexible and easier to reuse. Polymorphism in Python, achieved through method overriding or operator overloading, helps you write cleaner and more organized programs.