Explain Codes LogoExplain Codes Logo

Python function overloading

python
function-overloading
singledispatch
multipledispatch
Alex KataevbyAlex Kataev·Oct 14, 2024
TLDR

In Python, function overloading—the ability for a single function to have multiple behaviors depending on its inputs—is achieved differently than in other languages. Using the *args and **kwargs syntax, Python functions can accept variable numbers of arguments. We use if statements and the handy function isinstance() to differentiating between these behaviors. Here's a quick example:

def func(*args): # a string walks into a bar... if len(args) == 1 and isinstance(args[0], str): return "String: " + args[0] # ...followed by two integers elif len(args) == 2 and all(isinstance(arg, int) for arg in args): return "Sum: " + str(sum(args)) print(func("test")) # Output: String: test print(func(1, 2)) # Output: Sum: 3

This example uses len(args) to count the number of arguments and isinstance() to check their types. It's like a changeling, but for functions.

Function melanage with singledispatch

Python 3.4 gives us the functools.singledispatch decorator, which allows functions to behave differently based on their first argument's type—a kind of single-type function overloading. Define the @singledispatch function as the default case, then use the .register() attribute to add more function variations for other types:

from functools import singledispatch @singledispatch def func(arg): raise NotImplementedError("Unsupported type") @func.register(str) def _(arg): # the string can't resist tagging along return "String: " + arg @func.register(list) def _(arg): # the list had to list itself to fit in return "List: " + str(len(arg))

The referred function depends on the type of the first argument.

Jump into the multipledispatch pool

When dealing with functions that can have multiple signatures, consider the multipledispatch library. It's like a Swiss army knife for function overloading. Nonethless, it is not thread-safe in multi-threaded environments.

from multipledispatch import dispatch @dispatch(str) def func(arg): return "String: " + arg @dispatch(int, int) def func(x, y): # 'cause two integers walked in, we thought we'd add them up return "Sum: " + str(x + y) @dispatch(list) def func(arg): # if a list arrives, it gets a VIP treatment (length calculation) return "List: " + str(len(arg))

Stepping into the multimethods dimension

Another approach to function overloading is multimethods—a scheme where overloaded functions do not belong to a class or instance. They're the rebels of the function world, eschewing classes, inheritance, or excessive keyword arguments.