Python 3 如何用元编程和 typehints 实现类函数重载
Python 是有名的动态类型语言,本身不支持函数重载。
class Foo:
def add(self, a):
pass
def add(self, a, b):
# 会覆盖上面已经定义类函数
pass
然而使用元编程 (meta-programming) 和 python 3.5+ 的 type hints,可以实现类函数重载。
然后写了一个库:pover - Python OVERload
pip3 install pover
使用一个 metaclass OverloadMeta实现类函数重载。
#!/usr/bin/env python3
import unittest
import pover
class Foo(metaclass=pover.OverloadMeta):
def add(self, a: int):
return a + 1
def add(self, a: list):
return a + [1]
def add(self, a: set):
a.add(1)
return a
def minus(self, a: int):
return a - 1
def minus(self, a: str):
return a[:-1]
class OverloadTest(unittest.TestCase):
def test_overload(self):
foo = Foo()
self.assertEqual(foo.add(1), 2)
self.assertEqual(foo.add([0]), [0, 1])
self.assertEqual(foo.add({0}), {0, 1})
self.assertEqual(foo.minus(1), 0)
self.assertEqual(foo.minus(“hello”), “hell”)
具体实现原理在: https://github.com/idf/pover/blob/master/pover/core.py
GitHub 地址,请多指教: https://github.com/idf/pover
Python 3 如何用元编程和 typehints 实现类函数重载
在Python里实现真正的函数重载比较麻烦,因为Python不支持像C++那样的原生重载。不过我们可以用functools.singledispatch配合类型提示来模拟。
最简单的方案是用singledispatch装饰器:
from functools import singledispatch
from typing import Union
@singledispatch
def process(data):
raise NotImplementedError("Unsupported type")
@process.register
def _(data: int):
return f"Processing integer: {data}"
@process.register
def _(data: str):
return f"Processing string: {data}"
@process.register
def _(data: list):
return f"Processing list with {len(data)} items"
# 使用示例
print(process(42)) # Processing integer: 42
print(process("hello")) # Processing string: hello
print(process([1, 2, 3])) # Processing list with 3 items
对于类方法,可以用singledispatchmethod:
from functools import singledispatchmethod
class Processor:
@singledispatchmethod
def handle(self, data):
raise NotImplementedError("Unsupported type")
@handle.register
def _(self, data: int):
return f"Handling int: {data}"
@handle.register
def _(self, data: str):
return f"Handling str: {data}"
如果你想要更花哨的元编程方案,可以自己写装饰器来收集重载函数:
from inspect import signature
from typing import get_type_hints, Dict, Callable, Any
class Overload:
def __init__(self):
self._registry: Dict[tuple, Callable] = {}
def register(self, func: Callable):
sig = signature(func)
type_hints = get_type_hints(func)
key = tuple(type_hints.get(param, Any) for param in sig.parameters if param != 'self')
self._registry[key] = func
return self
def __call__(self, *args, **kwargs):
from typing import get_type_hints
arg_types = tuple(type(arg) for arg in args)
# 寻找匹配的函数
for key, func in self._registry.items():
if len(key) == len(arg_types) and all(
isinstance(arg, expected) if expected is not Any else True
for arg, expected in zip(arg_types, key)
):
return func(*args, **kwargs)
raise TypeError("No matching overload found")
# 使用示例
overload_func = Overload()
@overload_func.register
def func(x: int) -> str:
return f"int: {x}"
@overload_func.register
def func(x: str) -> str:
return f"str: {x}"
print(func(10)) # int: 10
print(func("hi")) # str: hi
不过说实话,Python的动态类型特性让重载没那么必要,通常用可选参数或者联合类型就能搞定。
总结:用singledispatch最省事。
了解过,singledispatch 没法 dispatch 类函数吧。singledispatch decorator renames module functions,这个机制并不使用类函数。
from functools import singledispatch
def add(a:object): pass
.register(int)
def _add(a: int): return a + 1
.register(list)
def _add(a: list): return a + [1]
.register(set)
def _add(a: set): a.add(1); return a
cases=[1, [0], {0}]
for case in cases: print(add(case))
实现上由于 python 是动态语言,传递对象是鸭子类型又可以变长参数,所以用上方法重载的机会不太多。
java 那个有名的 23 种设计模式,大约有 16 种设计模式在动态语言里面根本就不需要了。
single dispatch 是不错,但是不适合类函数吧?见#2
当然可以用在类函数了。
show me the code.
Decorator invocation 是在类函数 invocation 时执行的的,类函数 definition 的时候同函数名已经在 class construction 被覆盖了。
哟
当然要静态方法调用啊,直接 类名.方法()

