Python Tutorial
Method Overloading in Python
In Java or C++, method overloading means defining several methods with the same name but different parameter lists, and the compiler picks one based on the arguments. Python does not support this directly: a later definition with the same name simply replaces the earlier one.
This lesson shows what happens if you try, and the Pythonic ways to get the same flexibility: default and variable arguments, type-based dispatch with functools.singledispatch and singledispatchmethod, alternative constructors, and typing.overload for type checkers.
Why Classic Overloading Does Not Work
A class body is executed like normal code: def area(self, r) followed by def area(self, w, h) binds the name area twice, and only the last function survives. Calling it with one argument then raises TypeError.
Default Arguments and *args
Most overloading needs are met with optional parameters: def area(self, a, b=None) computes a square when b is missing and a rectangle otherwise. *args and **kwargs handle a variable number of arguments.
Dispatch on Type
functools.singledispatch turns a function into a generic function whose implementation is chosen by the type of the first argument; register implementations with @func.register. For methods, functools.singledispatchmethod dispatches on the first argument after self.
typing.overload
@typing.overload declares several signatures for type checkers and IDEs, followed by one real implementation. It documents the accepted combinations but performs no runtime dispatch.
Examples
The last definition wins
class Calculator:
def add(self, a, b):
return a + b
def add(self, a, b, c): # replaces the first add
return a + b + c
calc = Calculator()
print(calc.add(1, 2, 3))
try:
calc.add(1, 2)
except TypeError as e:
print("TypeError:", e)
6
TypeError: Calculator.add() missing 1 required positional argument: 'c'
Overloading with default arguments and *args
class Calculator:
def add(self, *numbers):
return sum(numbers)
def area(self, a, b=None):
return a * a if b is None else a * b
calc = Calculator()
print(calc.add(1, 2), calc.add(1, 2, 3, 4))
print(calc.area(5), calc.area(4, 6))
3 10
25 24
Type-based dispatch with singledispatch and singledispatchmethod
from functools import singledispatch, singledispatchmethod
@singledispatch
def describe(value):
return f"something: {value!r}"
@describe.register
def _(value: int):
return f"integer with {len(str(abs(value)))} digits"
@describe.register
def _(value: list):
return f"list of {len(value)} items"
print(describe(12345), "|", describe([1, 2]), "|", describe(3.5))
class Formatter:
@singledispatchmethod
def format(self, value):
return str(value)
@format.register
def _(self, value: float):
return f"{value:.2f}"
@format.register
def _(self, value: dict):
return ", ".join(f"{k}={v}" for k, v in value.items())
f = Formatter()
print(f.format(7), f.format(3.14159), f.format({"a": 1, "b": 2}))
integer with 5 digits | list of 2 items | something: 3.5
7 3.14 a=1, b=2
typing.overload for precise type hints
from typing import overload
@overload
def parse(value: str) -> int: ...
@overload
def parse(value: bytes) -> str: ...
def parse(value):
if isinstance(value, bytes):
return value.decode()
return int(value)
print(parse("42") + 1, parse(b"hello").upper())
43 HELLO
Common Mistakes
- Defining several methods with the same name expecting Java-style overloading.
- Writing long isinstance chains where singledispatch would be clearer.
- Expecting typing.overload to dispatch at runtime — it only informs type checkers.
- Using *args everywhere so the function signature no longer documents its inputs.
Key Points to Remember
- Python keeps only the last definition of a name; there is no classic overloading.
- Default arguments and *args/**kwargs cover most overloading needs.
- functools.singledispatch / singledispatchmethod dispatch on argument type.
- Alternative constructors (@classmethod) replace constructor overloading.
- typing.overload documents multiple signatures for type checkers.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.