Python 不支持函数重载,在同一个模块中声明同名方法不会报错,只会不停的覆盖,无论参数个数是否不同,最终只会保留最后一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
foo = 100 def foo(a): print('foo(a)') def foo(a, b): print('foo(a, b)') def foo(a: str): print('foo(a: str)') foo([8]) print(globals()['foo']) foo(3, 5) |
输出
foo(a: str)
<function foo at 0x107e74c10>
Traceback (most recent call last):
File ".../scratches/scratch_1.py", line 12, in <module>
foo(3, 5)
TypeError: foo() takes 1 positional argument but 2 were given
只有最后一个 foo
函数是存在的,在 globals() 只有一个 foo
那么 Python 要怎么实现函数重载呢?Python 3.4 在 functools 下添加了 singledispatch, Python 3.8 又继续加入了 singledispatchmethod。Python 的文档 https://docs.python.org/3/glossary.html#term-generic-function 和 PEP443 说它们的功用是支持单一分派的泛型函数。但本人觉得更像是函数重载。
为什么说是单分派(single dispatch)呢? 因为它只能根据第一个参数的类型选择要实际执行的函数。看下面的实例
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 |
from functools import singledispatch @singledispatch def foo(a): print(f"main {a}") print(hex(id(foo))) @foo.register(int) def _foo(a): print(f"{a} - 1") print(hex(id(_foo))) @foo.register(str) def _foo(a): print(f"{a} - 2") print(hex(id(_foo))) foo(1) foo('str') foo([]) breakpoint() |
在 IDE 中我们可以在 breakpoint()
处打个断点来调试,或者运行直接进入 pdb
控制台查看运行状态
输出为
0x103dbd790
0x103dbd8b0
0x103dbd940
1 - 1
str - 2
main []
pdb
(Pdb) foo.registry
mappingproxy({<class 'object'>: <function foo at 0x103da2c10>, <class 'int'>: <function _foo at 0x103dbd8b0>, <class 'str'>: <function _foo at 0x103dbd940>})
(Pdb) foo
<function foo at 0x103dbd790>
(Pdb) _foo
<function _foo at 0x103dbd940>
从这里我们就能发现它的实现方式,singledispatch 装饰的函数 foo 用 @foo.register(cls) 来注册声明的方法,并存储在 foo.registry 属性中,调用 foo(x) 时便根据第一个参数的类型进行分派,默认会调用在 foo.registry 中的 <class 'object'>
对应的实现上,即代理到被 @singledispatch 装饰的函数的实现
singledispatchmethod 的用法
参考 Python 官方文档的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Negator: @singledispatchmethod def neg(self, arg): raise NotImplementedError("Cannot negate a") @neg.register def _(self, arg: int): return -arg @neg.register def _(self, arg: bool): return not arg negator = Negator() print(negator.neg(1)) print(negator.neg(True)) |
执行后输出为
-1
False
那能不能根据构造实例时的类型来判断调用哪个 neg() 方法呢?大概想要实现的是
Negator(1).neg() -> 得到 -1
negator(True).neg() -> 得到 False
尝试写成下面那样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from functools import singledispatchmethod class Negator: def __init__(self, arg): self.arg = arg @singledispatchmethod def neg(self, arg): raise NotImplementedError("Cannot negate a") @neg.register(int) def _(self): return -self.arg @neg.register(bool) def _(self): return not self.arg print(Negator(1).neg()) print(Negator(True).neg()) |
执行后出错
method = self.dispatcher.dispatch(args[0].__class__)
IndexError: tuple index out of range
所以函数还是必须要有至少一个参数才能代理到正确的方法实现上去,虽然注册方法是没有问题的
result = {function} <function Negator.neg at 0x107a82700>
registry = {mappingproxy: 3} {<class 'object'>: <function Negator.neg at 0x107a82430>, <class 'int'>: <function Negator._ at 0x107a82820>, <class 'bool'>: <function Negator._ at 0x107a828b0>}
本文链接 https://yanbin.blog/python-overload/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。