通常来说,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 only
和 keyword 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 inspectfrom inspect import Parameterfrom typing import Any , TypeVar, Callable , ParamSpec, IteratorT = 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) 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 ) == FIRSTassert foo(3.14 ) == SECONDassert foo(1 , 2 ) == THIRD
就这样。