Skip to main content

Polymorphism: Operator Overloading in Python

 

Polymorphism

 

Poly -> Many

Morphs -> Forms

One name but multiple forms

 

Definition:

·         Polymorphism in Python's object-oriented programming is the ability of an object to take on many forms.

·         Specifically, it refers to the idea that different objects can be treated as if they are the same type of object, even if they are fundamentally different.

·         This concept of polymorphism can make our code more flexible and easier to work with, as we don't need to write separate functions for every different type of object we want to work with.

 

Example

For example, let's say we have two classes: Dog and Cat. Both classes have a method called speak(), but they behave differently. When we call speak() on a Dog object, it will bark, while calling speak() on a Cat object will make it meow.

 

Types of Polymorphism supported by Python

 

Duck Typing Polymorphism: This is a form of dynamic typing in which the type of an object is determined by its behavior or attributes rather than by its explicit type. This means that any object can be used as a parameter for a function as long as it has the necessary attributes or methods. For example, if an object has a "quack" method, it can be treated as a duck and passed as an argument to a function that expects a duck. Duck Typing can be a powerful tool for writing flexible, reusable code, but it can also be risky if you rely too much on object behavior without checking the object's type first.

 

Operator Overloading Polymorphism: Python allows you to overload operators such as +, -, *, and /, allowing you to use them with custom objects. For example, if you define the add() method in a class, you can use the + operator with objects of that class.

 

Function Overloading Polymorphism: Python does not support function overloading in the traditional sense, where you can have multiple functions with the same name but different parameters. However, you can achieve similar functionality by using default values for parameters or by using variable-length arguments (*args and **kwargs).

 

Method Overriding Polymorphism: This is a form of subtype polymorphism in which a subclass provides its own implementation of a method that is already defined in its parent class. When the method is called on an object of the subclass, the subclass's implementation is used instead of the parent class's implementation.

 

Abstract Base Classes Polymorphism: Python's built-in abc module allows you to define abstract base classes, which are classes that cannot be instantiated but define a common interface for their subclasses. This allows you to write code that operates on objects of different classes as long as they inherit from the same abstract base class.

 

Operator Overloading

 

Operator overloading is a feature in Python that allows you to define how operators such as +, -, *, /, ==, !=, <, >, <=, and >= behave with custom objects.

 

In Python, operators such as +, -, *, /, ==, !=, <, >, <=, and >= can be used with built-in data types like integers and strings. However, it's also possible to overload these operators for custom objects by defining special methods known as "magic" or "dunder" methods. These methods have double underscores (__) before and after their names, such as add() for the + operator.

 

 Overloading of Operators and its Magic methods:


Operator

Magic Method

Description

Addition (+)

__add__(self, other)

Adds two objects

Subtraction (-)

__sub__(self, other)

Subtracts two objects

Multiplication (*)

__mul__(self, other)

Multiplies two objects

Division (/)

__truediv__(self, other)

Divides two objects

Floor Division (//)

__floordiv__(self, other)

Performs floor division on two objects

Modulus (%)

__mod__(self, other)

Computes the remainder of two objects

Exponentiation (**)

__pow__(self, other)

Raises one object to the power of another object

Negation (-)

__neg__(self)

Returns the negative of an object

Absolute Value (abs())

__abs__(self)

Returns the absolute value of an object

Equality (==)

__eq__(self, other)

Tests for equality between two objects

Inequality (!=)

__ne__(self, other)

Tests for inequality between two objects

Less Than (<)

__lt__(self, other)

Tests if one object is less than another object

Greater Than (>)

__gt__(self, other)

Tests if one object is greater than another object

Less Than or Equal To (<=)

__le__(self, other)

Tests if one object is less than or equal to another object

Greater Than or Equal To (>=)

__ge__(self, other)

Tests if one object is greater than or equal to another object

 

 

 

Boolean (bool())

__bool__(self)

Converts an object to a Boolean value

 

Common shorthand operator overloading magic methods in Python:


Operator

Magic Method

Description

Addition Assignment (+=)

__iadd__(self, other)

Adds two objects and assigns the result to the first object

Subtraction Assignment (-=)

__isub__(self, other)

Subtracts two objects and assigns the result to the first object

Multiplication Assignment (*=)

__imul__(self, other)

Multiplies two objects and assigns the result to the first object

Division Assignment (/=)

__itruediv__(self, other)

Divides two objects and assigns the result to the first object

Floor Division Assignment (//=)

__ifloordiv__(self, other)

Performs floor division on two objects and assigns the result to the first object

Modulus Assignment (%=)

__imod__(self, other)

Computes the remainder of two objects and assigns the result to the first object

Exponentiation Assignment (**=)

__ipow__(self, other[, modulo])

Raises one object to the power of another object and assigns the result to the first object

Bitwise NOT (~)

__invert__(self)

Performs a bitwise NOT operation on an object

 

Example Program:

 

#Operator Overloading

class Person:

    def __init__(self,amount):

        self.amount = amount

    def __add__(self,other):

        return self.amount + other.amount

    def __sub__(self,other):

        return self.amount - other.amount

   

p1 = Person(100)

p2 = Person(50)

print("p1 + p2 = " , p1+p2)

print("p1 - p2 = " , p1-p2)

 

Output:

p1 + p2 =  150

p1 - p2 =  50


Program 2:

#Operator Overloading with more than 2 objects

class Person:

    def __init__(self,amount):

        self.amount = amount

    def __add__(self,other):

        total = self.amount + other.amount

        return Person(total)


    def __str__(self):

        return f"The Result is {self.amount}"

    

p1 = Person(500)

p2 = Person(50)

p3 = Person(200)

print(p1+p2+p3)

The Output is:

The Result is 750

Note: 

  • Whenever we are calling + operator, then __add__() method will be called.
  • +operator return type will become __add__() method return type
  • Whenever we are printing Person Object Reference, then __str__() method  will be called.
  • If we not providing this method(__str__()), then default implementation will be executed


Important Points to note when using operator overloading.

 

1.       The operator should be defined within the class: Operator overloading only works with class types, so the overloaded operator should be defined as a member function or a friend function of the class.

 

2.       The operator should be overloaded for a specific purpose: Overloading operators should be done for a specific purpose, such as performing mathematical operations or comparing objects.

 

3.       The overloaded operator's behavior should be consistent with its traditional use: When overloading an operator, it's important to ensure that the behavior of the operator is consistent with its traditional use. For example, the "+" operator should perform addition, not subtraction.

 

4.       The operator should return a new object: When an operator is overloaded, it should return a new object rather than modifying the operands.

 

5.       The operator should be implemented efficiently: When overloading an operator, it's important to ensure that the implementation is efficient, as operator overloading can be slower than regular function calls.

 

6.       The behavior of the overloaded operator should be well-documented: It's important to document the behavior of the overloaded operator to ensure that other developers can understand the intended behavior and use the operator correctly.

 

7.       Overloading should be used judiciously: Overloading too many operators can make code difficult to read and maintain, so overloading should be used judiciously. It's important to ensure that the overloaded operator adds clarity and simplicity to the code rather than making it more complex.

 

Self Assessment Questions:

 

1.       What is operator overloading in Python?

2.       Which dunder method is used to overload the "+" operator?

3.       What is the purpose of the "str" dunder method?

4.       Can the "in" operator be overloaded in Python?

5.       What is the difference between "eq" and "ne" dunder methods?

6.       How can you overload the ">" operator in Python?

7.       Is it possible to overload the "is" operator in Python? Why or why not?

8.       What is the "call" dunder method used for in Python?

9.       How can you overload the "[]" operator in Python?

10.   What is the difference between "lt" and "le" dunder methods?

 

Programming Exercises:

 

1.       Write a Python class called "Rectangle" that represents a rectangle with length and width dimensions. Overload the "+" operator to allow two Rectangle objects to be added together to create a new Rectangle with the combined dimensions. Overload the "==" operator to allow two Rectangle objects to be compared for equality based on their dimensions. Implement a method called "area" that returns the area of the rectangle.

 

2.       Create a class called Time that has separate int member data for hours, minutes and seconds. One constructor should initialize this data to 0. and another should initialize it to fixed values. A member function should display it, in 11:59:59 format. Write a program to add time of two objects by overloading '+' operator. 

 

3.       Create a class whose object represents a complex number (A complex number contains a real part and an imaginary part). Write a program so that it is possible to add two objects of this class and store the result in third object. 

 

4.       Write a Python class called "EmailAddress" that represents an email address. Overload the "==" operator to allow two EmailAddress objects to be compared for equality based on their email address strings. Overload the "+" operator to allow two EmailAddress objects to be concatenated together to create a new EmailAddress object with a combined email address.

 

5.       Create a Python program that extends the functionality (overloading) of the + operator to handle the addition of two different data types, such as strings and numbers.




Comments

Popular posts from this blog

Python OOPs Concepts: Using Variables and Methods

  Types of Variables in OOPs Python   Instance Variable Static Variable Local Variable   Object Level Variables Class Level Variables Method Level Variables When to use: For Every Object if you want Separate copy, use Instance Variables For all object one copy is required, use static variables Inside method, Just used for temporary requirement Where to Declare Inside the constructor method (in general) Within the class directly, outside of methods (in general)   Within the method only. How to Declare Within the constructor: Instance variables can be declared within the constructor method using the self .   Using default values : Instance variables can be assigned default values during initialization.   Outside the class: use object name.   ·          Within the class directly

Inheritance

Inheritance is a fundamental concept in object-oriented programming, which allows a class to inherit properties and methods from another class. There are several types of inheritance, including: Single Inheritance: In single inheritance, a subclass inherits properties and methods from a single parent class. The subclass is said to be derived from the parent class. Multiple Inheritance: Multiple inheritance allows a subclass to inherit properties and methods from multiple parent classes. In this case, the subclass is said to have multiple base classes. However, multiple inheritance can lead to complexity and ambiguity in the code. Multilevel Inheritance: Multilevel inheritance occurs when a subclass inherits properties and methods from a parent class, which in turn inherits from another parent class. In this case, the subclass is said to be derived from both the parent class and the grandparent class. Hierarchical Inheritance: Hierarchical inheritance occurs when multiple subclasses

Polymorphism: Method Overloading vs Method Overriding

  Method Overloading In object-oriented programming languages, method overloading enables a class to have several methods with the same name but different parameters. However, in Python, method overloading is not directly supported as opposed to languages such as Java or C++. This is because Python allows developers to define default arguments for their methods and pass arguments of any type to a method. This flexibility allows a single method to handle various types of arguments, eliminating the need for overloading.   However, there is a way to simulate method overloading in Python by using default argument values or variable length arguments and conditional statements. Here's an example: Program using default arguments:       Program using variable length arguments:   Multiple methods with Same Name: When we define multiple methods with same name, Python will consider the last defined method only. Python will not support method overloading. ( Why? Method overlo