MethodOverload - Function and Method Overloading for Python
A powerful Python library that brings function and method overloading to Python, allowing you to define multiple implementations of the same function with different type signatures. Overloading is resolved at runtime based on the types of arguments passed.
Why MethodOverload?
Python doesn't natively support function overloading like Java or C++. If you define two functions with the same name, the second one overwrites the first. MethodOverload solves this problem elegantly:
# Without MethodOverload - This doesn't work!
def add(a: int, b: int):
return a + b
def add(a: str, b: str): # Overwrites the previous function!
return f"{a} {b}"
# With MethodOverload - This works perfectly!
from methodoverload import overload
@overload
def add(a: int, b: int):
return a + b
@overload
def add(a: str, b: str):
return f"{a} {b}"
print(add(1, 2)) # Output: 3
print(add("Hello", "World")) # Output: Hello World
Features
✨ Simple & Intuitive - Just use the @overload decorator
✨ Type-Safe - Automatically matches the correct implementation based on type hints
✨ Instance Methods - Full support for instance methods with metaclass integration
✨ Class Methods - Works seamlessly with @classmethod
✨ Static Methods - Compatible with @staticmethod
✨ Caching - Fast resolution with built-in caching mechanism
✨ Clear Error Messages - Helpful error messages when no matching overload is found
Installation
Install from PyPI:
pip install methodoverload
Or from source:
git clone https://github.com/mohdcodes/pyoverload.git
cd methodoverload
pip install -e .
Quick Start
1. Basic Function Overloading
from methodoverload import overload
@overload
def process(data: int):
return data * 2
@overload
def process(data: str):
return data.upper()
@overload
def process(data: list):
return len(data)
print(process(5)) # Output: 10
print(process("hello")) # Output: HELLO
print(process([1, 2, 3])) # Output: 3
2. Instance Methods with Metaclass
from methodoverload import overload, OverloadMeta
class Calculator(metaclass=OverloadMeta):
@overload
def compute(self, a: int, b: int):
return a + b
@overload
def compute(self, a: str, b: str):
return f"{a}{b}"
@overload
def compute(self, a: list, b: list):
return a + b
calc = Calculator()
print(calc.compute(1, 2)) # Output: 3
print(calc.compute("Hello", "World")) # Output: HelloWorld
print(calc.compute([1, 2], [3, 4])) # Output: [1, 2, 3, 4]
3. Class Methods
from methodoverload import overload, OverloadMeta
class Greeter(metaclass=OverloadMeta):
@overload
@classmethod
def greet(cls, name: str):
return f"Hello {name}"
@overload
@classmethod
def greet(cls, count: int):
return f"Greeting #{count}"
print(Greeter.greet("Alice")) # Output: Hello Alice
print(Greeter.greet(42)) # Output: Greeting #42
4. Static Methods
from methodoverload import overload, OverloadMeta
class Math(metaclass=OverloadMeta):
@overload
@staticmethod
def multiply(a: int, b: int):
return a * b
@overload
@staticmethod
def multiply(a: float, b: float):
return round(a * b, 2)
print(Math.multiply(3, 4)) # Output: 12
print(Math.multiply(3.5, 2.5)) # Output: 8.75
How It Works
MethodOverload uses type hints to determine which implementation to call:
- Registration - Each
@overloaddecorated function is registered with its type signature - Resolution - When called, MethodOverload examines the argument types
- Matching - Finds the implementation whose type signature matches the arguments
- Execution - Calls the matching implementation
- Caching - Results are cached for performance
Type Matching Rules
- Arguments are matched against type hints using
isinstance() - The first matching overload is called
- If no overload matches,
NoMatchingOverloadErroris raised
from methodoverload import overload
@overload
def process(data: str):
return f"String: {data}"
@overload
def process(data: int):
return f"Integer: {data}"
# This works!
process("hello") # Output: String: hello
process(42) # Output: Integer: 42
# This raises NoMatchingOverloadError
process(3.14) # No matching overload for float!
Advanced Usage
Multiple Type Parameters
from methodoverload import overload
@overload
def convert(value: int, to_type: str):
return str(value)
@overload
def convert(value: str, to_type: int):
return int(value)
@overload
def convert(value: list, to_type: tuple):
return tuple(value)
print(convert(42, "str")) # Output: "42"
print(convert("100", "int")) # Output: 100
print(convert([1, 2, 3], tuple)) # Output: (1, 2, 3)
Combining with Other Decorators
from methodoverload import overload
@overload
def cached_process(data: int):
print("Processing integer...")
return data ** 2
@overload
def cached_process(data: str):
print("Processing string...")
return data.upper()
# Each call with the same type is cached for performance
print(cached_process(5)) # Processing integer...
print(cached_process(5)) # (cached - no print)
Error Handling
When no matching overload is found, you get a helpful error message:
from methodoverload import overload, NoMatchingOverloadError
@overload
def add(a: int, b: int):
return a + b
try:
add("1", "2") # No string overload defined
except NoMatchingOverloadError as e:
print(e)
# Output: No matching overload found for 'add' with args=('1', '2'), kwargs={}
Examples
See the examples/ directory for more detailed examples:
- basic.py - Free function overloading
- classmethod.py - Class method overloading
- staticmethod.py - Static method overloading
- types.py - Complex type examples
Run an example:
python -m examples.basic
Architecture
Core Components
- OverloadedFunction - Container for multiple implementations with dispatch logic
- @overload Decorator - Registers function implementations
- OverloadMeta Metaclass - Merges overloads in class definitions
- OverloadCache - Caches resolved overloads for performance
Design Principles
- Framework-agnostic - No dependencies, works with any Python code
- Type-driven - Uses type hints for dispatch
- Descriptor protocol - Integrates seamlessly with Python's method binding
- Efficient - Built-in caching for repeated calls
Limitations & Best Practices
Current Limitations
- Overloads are resolved at runtime (not compile-time)
- Only supports positional and keyword arguments
- Type matching uses
isinstance()(no generic types likeList[int])
Best Practices
✓ Always use type hints for all parameters
✓ Keep implementations focused and simple
✓ Use descriptive function names
✓ Test all overload combinations
✓ Document which types are supported
# Good
@overload
def process(data: str) -> str:
"""Process string data and return uppercase."""
return data.upper()
# Avoid - Missing type hints
@overload
def process(data):
return data.upper()
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Authors
- Mohd Arbaaz Siddiqui - Original author
- See Contributors for the full list
Support
Changelog
Version 0.1.0 (Current)
- Initial release
- Free function overloading
- Instance method overloading with OverloadMeta
- Class method and static method support
- Built-in caching for performance
- Type hint-based dispatch
- Comprehensive error messages
Citation
If you use MethodOverload in your research or project, please cite:
@software{methodoverload2024,
title={MethodOverload: Function and Method Overloading for Python},
author={Siddiqui, Mohd Arbaaz},
year={2024},
url={https://github.com/mohdcodes/pyoverload}
}
Made with ❤️ by MOHD ARBAAZ SIDDIQUI
Metadata
Release files for methodoverload 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| methodoverload-0.1.7.tar.gz | 15.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| methodoverload-0.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.7 kB
Release files / methodoverload-0.1.7.tar.gz
| Download URL | methodoverload-0.1.7.tar.gz |
|---|---|
| Size | 15.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e909536b9356d3958f63a8059de5eb65a1e201723013847712d80ca052ec122f
|
|
BLAKE2b-256 checksum How to use checksums |
8a24381b81ee021ef58b374567d4a4d93aff5d3a56892eca06c85bfd81091666
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Release files / methodoverload-0.1.7-py3-none-any.whl
| Download URL | methodoverload-0.1.7-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa550278ab545dd77dbc7cf31cde418b82cfb80d9f44e1885bb002dd1f97e281
|
|
BLAKE2b-256 checksum How to use checksums |
a791be01b31f27c79bec9149a1e6f064f19e64d217fd127926301a27bdba70a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Security for methodoverload 0.1.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.