Python - Polymorphism
*
and **
syntax in method overloading?In Python, the *
(single asterisk) and **
(double asterisk) syntax is used to define variable-length argument lists in function definitions. These are commonly referred to as *args
(for variable-length positional arguments) and **kwargs
(for variable-length keyword arguments).
Purpose of *args
and **kwargs
:
The purpose of *args
and **kwargs
in method overloading is to allow functions to accept a variable number of arguments. This flexibility is especially useful when you want to define functions that can handle different numbers of parameters, providing more versatility and making the code more readable and concise.
Example Program:
def add_numbers(*args):
total = 0
for num in args:
total += num
return total
def concatenate_strings(**kwargs):
result = ""
for key, value in kwargs.items():
result += f"{key}: {value} "
return result.strip()
# Example using *args
sum_result = add_numbers(2, 3, 4, 5)
# Example using **kwargs
concat_result = concatenate_strings(first_name="John", last_name="Doe", age=30)
print(sum_result)
print(concat_result)
Output:
14 first_name: John last_name: Doe age: 30
In this example, the add_numbers
function uses *args
to accept a variable number of positional arguments and calculates their sum. The concatenate_strings
function uses **kwargs
to accept a variable number of keyword arguments and concatenates them into a string. These functions can be called with different numbers of arguments, showcasing the flexibility provided by *args
and **kwargs
.
Using these variable-length argument lists can simplify function definitions and make the code more adaptable to different use cases.
Method overriding is a concept in object-oriented programming (OOP) where a subclass provides a specific implementation for a method that is already defined in its superclass. This allows the subclass to provide its own version of the method, giving it a chance to customize or extend the behavior inherited from the superclass.
Example Program:
class Animal:
def speak(self):
return "Animal speaks"
class Dog(Animal):
def speak(self):
return "Dog barks"
class Cat(Animal):
def speak(self):
return "Cat meows"
# Example of Method Overriding
def animal_speak(animal):
return animal.speak()
dog = Dog()
cat = Cat()
print(animal_speak(dog))
print(animal_speak(cat))
Output:
Dog barks Cat meows
In this example, we have a base class Animal
with a method speak
. The Dog
and Cat
classes are subclasses of Animal
and both override the speak
method with their own implementations.
The animal_speak
function takes an object of type Animal
and calls its speak
method. The actual implementation of the speak
method is determined at runtime based on the type of the object passed to the function. This demonstrates the concept of method overriding in Python.
Method overriding allows for polymorphic behavior, where objects of different types (subclasses) can be treated as objects of the same type (superclass) while executing the same method.
Python does not support traditional function overloading like some other languages, but you can achieve a form of polymorphism using default values for function parameters. By providing default values, a function can be called with different numbers of arguments, resulting in polymorphic behavior.
Example Program:
def add_numbers(a, b=0, c=0):
return a + b + c
# Example of Polymorphism using Function Overloading
result1 = add_numbers(2, 3)
result2 = add_numbers(2, 3, 4)
print(result1)
print(result2)
Output:
5 9
In this example, the add_numbers
function is designed to take three parameters, but the second and third parameters have default values of 0. This allows the function to be called with different numbers of arguments. When called with two arguments, the function uses the default value for the third parameter, and when called with three arguments, it uses the provided values for all parameters.
While this approach does not provide the same level of flexibility as true function overloading in some other languages, it does allow for a level of polymorphism in Python by accommodating different numbers of arguments in a function call.
Abstract classes and methods play a crucial role in achieving polymorphism and abstraction in object-oriented programming (OOP). An abstract class is a class that cannot be instantiated and is meant to be subclassed. Abstract methods, defined in an abstract class, are methods that must be implemented by any concrete (non-abstract) subclass. This encourages a consistent interface among different classes, contributing to polymorphic behavior.
Example Program:
from abc import ABC, abstractmethod
# Abstract Class with Abstract Method
class Shape(ABC):
@abstractmethod
def area(self):
pass
# Concrete Subclass implementing Abstract Method
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius**2
# Concrete Subclass implementing Abstract Method
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side**2
# Function demonstrating polymorphism with abstract classes
def print_area(shape):
return shape.area()
# Creating objects of concrete subclasses
circle = Circle(5)
square = Square(4)
# Using polymorphism with abstract classes
area1 = print_area(circle)
area2 = print_area(square)
print(area1)
print(area2)
Output:
78.5 16
In this example, the Shape
class is an abstract class with an abstract method area
. The Circle
and Square
classes are concrete subclasses of Shape
and implement the area
method.
The print_area
function demonstrates polymorphism by accepting any object of type Shape
or its subclasses. This allows the function to work with different types of shapes without knowing their specific implementations, showcasing the power of abstract classes and methods in achieving polymorphism and abstraction.