ovld: Overloading Python Functions
Ovld is a Python library that provides fast and feature-rich multiple dispatch for functions using type annotations. Unlike Python's built-in `functools.singledispatch`, `ovld` supports dispatching on multiple arguments, custom predicates, and value-based dispatch. It aims to simplify code that would otherwise rely on complex `if-elif` chains or `isinstance` checks for different argument types. The library is actively maintained, with the current version being 0.5.15, and offers performance superior to other multiple dispatch libraries.
Warnings
- gotcha When defining recursive `ovld` functions, always use `recurse(...)` instead of directly calling the function by its name (e.g., `my_func(...)`). `recurse` is specially designed to work with `ovld`'s variant system, ensuring that recursive calls correctly dispatch to the appropriate variant of the current `ovld` object. Direct calls will bypass this mechanism.
- gotcha Dependent types created with `ovld.dependent.Dependent` require a type bound as their first argument (e.g., `Dependent[int, lambda n: n > 0]`). The provided check function is applied to the *value* of the argument at runtime, not its static type. Incorrectly specifying the type bound or the check predicate can lead to unexpected dispatch behavior or runtime errors.
- gotcha When using `ovld` to dispatch on generic collection types (e.g., `list[str]`), `ovld` currently only checks the type of the *first element* of the collection. It does not perform a full validation of all elements within the collection against the generic type parameter. This can lead to a dispatch if the first element matches, even if subsequent elements do not.
- gotcha The `postprocess` argument in the `@ovld` decorator (or `ovld.dispatch`) only applies to the *top-level* call of the overloaded function. Intermediate recursive calls made via `recurse()` will *not* have their results processed by the `postprocess` function.
Install
-
pip install ovld
Imports
- ovld
from ovld import ovld
- recurse
from ovld import ovld, recurse
- Dependent
from ovld.dependent import Dependent
- Literal
from typing import Literal
Quickstart
from ovld import ovld, recurse
from typing import Literal
@ovld
def process(x: str):
return f"Processing string: {x!r}"
@ovld
def process(x: int):
return f"Processing integer: {x}"
@ovld
def process(x: int, y: int):
return f"Processing two integers: {x}, {y}"
@ovld
def process(x: Literal[0]):
return "Special case: zero"
# Example of recursive overload
@ovld
def add_nested(x: list, y: list):
return [recurse(a, b) for a, b in zip(x, y)]
@ovld
def add_nested(x: int, y: int):
return x + y
assert process("hello") == "Processing string: 'hello'"
assert process(10) == "Processing integer: 10"
assert process(1, 2) == "Processing two integers: 1, 2"
assert process(0) == "Special case: zero"
assert add_nested([1, 2], [3, 4]) == [4, 6]
assert add_nested([1, [2]], [3, [4]]) == [4, [6]]