[TOC]

方法重载

以相同的方法名定义不同的方法,在不同条件下自动选择正确的版本进行调用,这就被称为方法重载。方法重载(Method Overloading)是面向对象的编程语言中经常用到的一个特性

在Python这种动态语言中,没有严格的类型约束,也没有重载的概念。

方法重载的过程简单来说,就是对不同的输入进行检查,然后派发给不同代码。

利用参数类型和数量实现方法重载

在Python中,两个或多个方法不能有相同的名字,所以在方法内进行重载

1
2
3
4
5
6
7
8
9
10
11
def fun(a,b,c=None):
if c is not None: #检查
print(a,b,c) #派发
else:
if type(a) == int: #检查
print(a+b) #派发
else:
print(a,b)

#在此例中,参数数量有(a,b)和(a,b,c)的不同,我们用if检查是否有c参数,然后执行if下的代码,实现派发
#参数类型有a:int和a:其他类型,也是用if type(a)检查a的参数类型,然后实现派发

这样做的弊端是,没有像Java重载那样,定义多个同名的方法,所有重载的代码都在一个方法中,如果重载更多更复杂,后续修改和添加也是一个问题,不像java重载那样能快速清晰的定位到。

利用字典映射实现方法重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def fun(a, b, c=None):
# 重载方法
def add_int(a, b, c):
return a + b

# 派发字典
dispatch_dict = {
(int, int, type(None)): lambda x,y,z:add_int(x,y,z),
(float, float, type(None)): lambda x,y,z: float(x + y),
(str, str, str): lambda x,y,z: x + y + z,
}
# 检查
key = (type(a), type(b), type(c))
return dispatch_dict[key](a, b, c)

#在此例中,我们通过检查全部参数的类型,再与派发字典中的key比对,就能得到应该派发的方法。

这样做就比上面的方法简单明了许多,很多地方与Java重载已经大差不差了,派发字典能清晰查看存在的重载方法,后续添加也更方便,可读性也大大提升。

利用装饰器实现方法重载

单派发

functools包中带有singledispatch单派发装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
from functools import singledispatch

@singledispatch
def add(a, b):
return a + b

@add.register(str)
def _(a, b):
return a + b

@add.register(float)
def _(a, b):
return float(a + b)

Singledispatch仅支持单个参数的函数重载

多派发

python没有提供多派发的装饰器,需要导入外部的包

1
pip install multipledispatch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from multipledispatch import dispatch

@dispatch(int,int,int)
def fun(a,b,c):
result = a + b +c
return result

@dispatch(float,float,float)
def fun(a,b,c):
result = float(a + b +c)
return result

multiply(3,7,8)
multiply(3.6,5.9,9.9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 装饰器实现
# dispatch
def overload(func):
funcs = {}

def wrapper(*args):
key = tuple(arg.__class__ for arg in args)
if key in funcs:
return funcs[key](*args)
else:
raise TypeError('No matching function found.')

def register(signiture, func):
funcs[signiture] = func

wrapper.register = register

return wrapper

#从该装饰器可以看出,该方法和字典类似,都设有一个注册表,然后根据数据类型进行分发