通常来说,Python 中的重载依赖 typing.overload,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import overload, Any


@overload
def foo(a: int) -> None: ...


@overload
def foo(a: float) -> None: ...


def foo(a: Any) -> None:
if isinstance(a, int):
return

if isinstance(a, float):
return

raise TypeError('...')

本文要实现的是借助 inspect 实现一个运行时帮助判断类型并调用指定函数的工具。

实现

为了简化逻辑,我们这里只支持普通的参数,也就是不支持 positional onlykeyword only 参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import inspect
from inspect import Parameter
from typing import Any, TypeVar, Callable, ParamSpec, Iterator


T = TypeVar('T')
P = ParamSpec('P')


def get_params(f: Callable[P, T]) -> Iterator[Parameter]:
yield from inspect.signature(f).parameters.values()


def match(f: Callable[P, T], args: tuple[Any, ...]) -> bool:
params = list(get_params(f))

if len(params) != len(args):
return False

return all(
isinstance(args[index], param.annotation)
for index, param in enumerate(params)
)


class Overload:
def __init__(self) -> None:
self.callables: set[Callable[..., T]] = set()

def __call__(self, *args: Any) -> Any:
for f in self.callables:
if match(f, args):
return f(*args)
raise TypeError(f'unmatched arguments {args}')

def register(self, f: Callable[P, T]) -> 'Overload':
for p in get_params(f):
if p.kind != Parameter.POSITIONAL_OR_KEYWORD:
raise TypeError('only allow normal arguments')
if not isinstance(p.annotation, type):
raise TypeError('annotations must be real types')
self.callables.add(f) # type: ignore
return self

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
FIRST = 1
SECOND = 2
THIRD = 3


foo = Overload()


@foo.register
def foo(a: int) -> int:
print(f'calling foo with an integer: {a}')
return FIRST


@foo.register
def foo(a: float) -> int:
print(f'calling foo with a float: {a}')
return SECOND


@foo.register
def foo(a: int, b: int) -> int:
print(f'calling foo with two integers: {a}, {b}')
return THIRD


assert foo(1) == FIRST
assert foo(3.14) == SECOND
assert foo(1, 2) == THIRD

就这样。