Python Programming Exercises: Examples of achieving Function Overloading

Examples of Function Overloading in Python

Python does not support traditional function overloading as seen in languages like C++ or Java. However, Python allows us to achieve similar functionality using techniques such as default arguments, variable-length arguments, and type checking within a single function. Below are some examples demonstrating these concepts.

Example 1: Function with Default Arguments


def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

# Example usage
print(greet("John"))  # Output: Hello, John!
print(greet("Jane", "Hi"))  # Output: Hi, Jane!

Description: In this example, the greet function can be called with one or two arguments. If only the name is provided, the function uses the default greeting of "Hello". This mimics overloading by allowing different numbers of arguments.

Unique Feature: This demonstrates how default arguments can be used to provide different behaviors based on the number of arguments passed to the function.

Example 2: Function with Variable-Length Arguments


def add(*numbers):
    return sum(numbers)

# Example usage
print(add(1, 2))  # Output: 3
print(add(1, 2, 3, 4))  # Output: 10

Description: The add function accepts any number of numerical arguments and returns their sum. The *numbers syntax allows the function to accept a variable number of arguments, mimicking function overloading.

Unique Feature: This example shows how Python's *args syntax allows functions to handle an arbitrary number of arguments, providing flexibility similar to function overloading.

Example 3: Function with Type Checking


def multiply(a, b):
    if isinstance(a, str) and isinstance(b, int):
        return a * b
    elif isinstance(a, int) and isinstance(b, int):
        return a * b
    else:
        raise ValueError("Invalid types for multiply function")

# Example usage
print(multiply(2, 3))  # Output: 6
print(multiply("abc", 3))  # Output: abcabcabc

Description: The multiply function behaves differently depending on the types of arguments passed. If both arguments are integers, it performs multiplication. If the first argument is a string and the second is an integer, it repeats the string. This mimics overloading by manually checking argument types.

Unique Feature: This implementation highlights how type checking within a function can be used to simulate overloading based on argument types, providing different behavior for different inputs.

Example 4: Function with Keyword Arguments


def calculate_area(shape, **dimensions):
    if shape == "rectangle":
        return dimensions["length"] * dimensions["width"]
    elif shape == "circle":
        return 3.14159 * (dimensions["radius"] ** 2)
    else:
        raise ValueError("Unknown shape")

# Example usage
print(calculate_area("rectangle", length=5, width=3))  # Output: 15
print(calculate_area("circle", radius=4))  # Output: 50.26544

Description: The calculate_area function uses keyword arguments (**dimensions) to handle different shapes and their respective parameters. Depending on the shape argument, the function calculates the area of a rectangle or a circle.

Unique Feature: This example demonstrates the flexibility of keyword arguments in Python, allowing a function to handle different sets of parameters and perform different calculations based on the input.

Example 5: Function Overloading with Method Dispatching


from functools import singledispatch

@singledispatch
def process(data):
    raise NotImplementedError("Unsupported type")

@process.register(int)
def _(data):
    return f"Processing integer: {data}"

@process.register(str)
def _(data):
    return f"Processing string: {data}"

@process.register(list)
def _(data):
    return f"Processing list of length {len(data)}"

# Example usage
print(process(10))  # Output: Processing integer: 10
print(process("hello"))  # Output: Processing string: hello
print(process([1, 2, 3]))  # Output: Processing list of length 3

Description: The singledispatch decorator from the functools module allows you to define a generic function and register different implementations for different types. This provides a clean way to achieve function overloading based on argument type.

Unique Feature: This example introduces the singledispatch decorator, which provides a more formal way to achieve function overloading in Python by dispatching method calls based on the type of the first argument.