The Ultimate Guide to Implement Function Overloading in Python

  tr_cn        2024-11-14 22:29:17       559        0    

Introduction

When it comes to function overloading, those who have learned Java should be familiar with it. One of the most common uses is logging, where different overloaded functions are called for different parameters. So, how can we implement function overloading in Python?

Concept of Overloading

Function overloading allows multiple functions with the same name to exist within the same scope, but with different parameter lists. Although many programming languages (like Java and C++) support function overloading, Python’s design philosophy does not support this feature directly.

Without Overloading

Let's look at an example of implementing a log function without overloading:

from attr import dataclass


@dataclass
class MyException(Exception):
    msg: str


def log(message):
    if isinstance(message, MyException):
        print(message.msg)
    elif isinstance(message, str):
        print(message)
    else:
        print(f'invalid message:{message}')


log(MyException('my exception'))
log('str exception')
log(1111)

Output:

my exception
str exception
invalid message:1111

Without overloading, we need to write a lot of conditional code to check the type of each argument.

Using Overloading

from functools import singledispatch
from attr import dataclass

@dataclass
class MyException(Exception):
    msg: str

@singledispatch
def log(message):
    print(f'invalid message:{message}')

@log.register
def _(message: MyException):
    print(message.msg)

@log.register
def _(message: str):
    print(message)

log(MyException('my exception'))
log('str exception')
log(1111)

Output:

 

As shown in the code above, using overloading makes the code much cleaner, removing the need for the previous `if-else` checks. Each overloaded function outputs a log based on its corresponding type, and when calling the function, the argument type is automatically detected, which then invokes the respective overloaded function.

my exception
str exception
invalid message:1111

If a new type needs to be added, we can simply add a new overloaded function, significantly simplifying the process. For example:

from functools import singledispatch
from attr import dataclass

@dataclass
class MyException(Exception):
    msg: str

@singledispatch
def log(message):
    print(f'invalid message:{message}')

@log.register
def _(message: MyException):
    print(message.msg)

@log.register
def _(message: str):
    print(message)

@log.register
def _(message: int):
    print(f'int message:{message}')

log(MyException('my exception'))
log('str exception')
log(1111)

Output:

my exception
str exception
int message:1111

Overloaded Implementation

Summary of Using functools.singledispatch for overloading in Python based on above code

  1. Define a Function with the @singledispatch Decorator: Begin by creating a base function and apply the @singledispatch decorator. This function will handle cases where no specific overload is registered for the type.
  2. Add Overloaded Functions: For each type you want to handle differently, define functions with an underscore (_) as the function name. Since each overloaded function will have the same name, using underscores keeps the names simple and consistent.
  3. Specify the Parameter Type for Each Overload: In each overloaded function, set the function parameter with a specific type hint. This type hint determines which function will be called based on the argument’s type.
  4. Test the Function: Call the function with various argument types to ensure the correct overload executes.

About @dataclass

You might notice that custom exception classes in the examples are decorated with @dataclass. Introduced in Python 3.6, @dataclass automatically generates special methods such as __init__() and __repr__() for the class, similar to Java’s @Data annotation from Lombok. 

Here's an example:

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

This removes the need to manually write methods like __init__() like below as they are auto-generated by @dataclass, simplifying the code and boosting development efficiency. 

def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand

More details can be found on its official documentation.

Conclusion

While Python does not support traditional function overloading, functools.singledispatch provides a way to implement similar functionality. Keep in mind that only the type of the first parameter is considered for overloading; type changes in subsequent parameters are ignored.

Reference: https://segmentfault.com/a/1190000045454704

PYTHON  OVERLOADING  JAVA  GUIDE 

       

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

Developer and product manager

A man flying in a hot air balloon suddenly realizes he’s lost. He reduces height and spots a man down below. He lowers the balloon further and shouts to get directions, "Excuse me, can you tell me where I am?" The man below says: "Yes. You're in a hot air balloon, hovering 30 feet above this