Python - Polymorphism
__len__
and __getitem__
methods for polymorphism.The __len__
and __getitem__
methods are special methods in Python that can be implemented in a class to enable specific behavior when using functions like len
and accessing items using indexing (e.g., obj[index]
). Implementing these methods allows for polymorphic behavior, enabling objects of different classes to be used in a uniform way.
Example Program:
class CustomList:
def __init__(self, items):
self.items = items
# Implementation of __len__ method
def __len__(self):
return len(self.items)
# Implementation of __getitem__ method
def __getitem__(self, index):
if 0 <= index < len(self.items):
return self.items[index]
else:
raise IndexError("Index out of range")
# Creating an object of the CustomList class
custom_list = CustomList([1, 2, 3, 4, 5])
# Using polymorphism with len function
length = len(custom_list)
# Using polymorphism with indexing
element = custom_list[2]
print(length)
print(element)
Output:
5 3
In this example, the CustomList
class implements the __len__
method to define the behavior of the len
function and the __getitem__
method to define the behavior of indexing. This allows instances of the CustomList
class to be used polymorphically with these standard Python functions.
By implementing these special methods, you enable objects of your class to be treated in a way similar to built-in types like lists or strings, contributing to a consistent and intuitive interface for different types of objects in your code.
Polymorphism with built-in functions like len()
can be achieved by implementing the special method __len__
in a class. By defining this method, you can customize how the len()
function behaves when called on an instance of your class, allowing for polymorphic behavior.
Example Program:
class CustomContainer:
def __init__(self, items):
self.items = items
# Implementation of __len__ method for polymorphism
def __len__(self):
return len(self.items)
# Creating objects of different classes
list_object = [1, 2, 3, 4, 5]
custom_container_object = CustomContainer([1, 2, 3, 4, 5])
# Using polymorphism with len function
length_of_list = len(list_object)
length_of_custom_container = len(custom_container_object)
print(length_of_list)
print(length_of_custom_container)
Output:
5 5
In this example, the CustomContainer
class implements the __len__
method, allowing instances of this class to be used polymorphically with the len()
function. Both a standard list and an instance of CustomContainer
can be passed to len()
, demonstrating polymorphic behavior.
By defining the __len__
method in your class, you enable built-in functions like len()
to work seamlessly with instances of your class, contributing to a more consistent and versatile codebase.
Polymorphic behavior with different data types refers to the ability of a function or method to work seamlessly with various types of data. This is achieved through polymorphism, a fundamental concept in object-oriented programming (OOP), where objects of different types can be treated as instances of a common base type. This allows for code that is more flexible, adaptable, and reusable.
Example Program:
class MathOperations:
@staticmethod
def add(x, y):
return x + y
# Using polymorphic behavior with different data types
result1 = MathOperations.add(5, 3) # Adding integers
result2 = MathOperations.add("Hello, ", "World") # Concatenating strings
result3 = MathOperations.add([1, 2, 3], [4, 5, 6]) # Concatenating lists
print(result1)
print(result2)
print(result3)
Output:
8 Hello, World [1, 2, 3, 4, 5, 6]
In this example, the MathOperations
class defines a static method add
that works with different data types. The method is polymorphic and can be used to add integers, concatenate strings, and concatenate lists. The code demonstrates how the same method can exhibit different behavior based on the data types of its arguments.
Polymorphic behavior with different data types promotes code reuse and flexibility, allowing functions or methods to be applied to a wide range of inputs. This concept is particularly powerful in OOP, where classes and objects can exhibit polymorphism through inheritance, method overriding, and operator overloading.
The functools.singledispatch
decorator in Python is used to define a single-dispatch function, allowing you to create polymorphic behavior for a function based on the type of the first argument. It provides a way to dispatch different implementations of a function based on the type of the first argument, promoting flexibility and maintainability.
Example Program:
from functools import singledispatch
# Using singledispatch to create a polymorphic function
@singledispatch
def process_data(data):
raise NotImplementedError("Unsupported data type")
# Implementations for specific data types
@process_data.register(int)
def _(data):
return f"Processing integer: {data}"
@process_data.register(str)
def _(data):
return f"Processing string: {data}"
@process_data.register(list)
def _(data):
return f"Processing list: {data}"
# Example usage of the polymorphic function
result1 = process_data(42)
result2 = process_data("Hello, World!")
result3 = process_data([1, 2, 3])
print(result1)
print(result2)
print(result3)
Output:
Processing integer: 42 Processing string: Hello, World! Processing list: [1, 2, 3]
In this example, the process_data
function is decorated with @singledispatch
, and specific implementations are registered for different data types using the @process_data.register
decorator. When the function is called, the appropriate implementation is selected based on the type of the first argument.
The functools.singledispatch
decorator provides a convenient way to create polymorphic functions that can handle different types of data in a clean and extensible manner. It promotes a modular approach to handling different cases and encourages the addition of new implementations for new data types without modifying the original function.