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