python的函数是一等公民,原则上或者说实质上并不像C++和java一样支持函数重载,但是可以借助functools.singledispatch装饰器来实现类似于函数重载的功能。functools.singledispatch 是Python的一个装饰器,它允许你将一个普通的函数变成一个单分派泛函数(generic function)。这意味着基于第一个参数的类型,你可以为同一个函数定义多个实现。它是Python对单分派泛型函数的支持,这种函数可以根据单个参数的类型来决定调用哪个具体的实现。
在讲解如何实现python函数的重载之前,有必要先解释重载所能解决的问题,下面的函数将根据参数的类型来实现不同的逻辑
def calc(x:Any, y:Any) -> Any:
if isinstance(x, int):
print("x和y都是int")
elif isinstance(x, float):
print("x和y都是float")
else:
raise NotImplementedError(f"calc not implemented for {type(x)} and {type(y)}")
return x + y
在函数内部,通过对参数x的类型进行判断来决定走哪种处理逻辑,随着x类型的增加,if 语句的逻辑分支也就会增多,函数的复杂度会提升,降低了代码的可维护性,重载就能很好的解决这个问题,重载后的函数可以根据参数的类型自动决定调用某个具体的实现。
下面是一个示例
from functools import singledispatch
from typing import Any
@singledispatch
def calc(x: Any, y: Any) -> Any:
raise NotImplementedError(f"calc not implemented for {type(x)} and {type(y)}")
@calc.register
def calc_int(x: int, y: int) -> int:
print("x和y都是int")
return x + y
@calc.register
def calc_float(x: float, y: float) -> float:
print("x和y都是float")
return x + y
calc(1, 2)
calc(3.4, 5.6)
如果传入的参数与注册的函数都不匹配,则会执行calc函数
calc('', 3)
程序会抛异常
NotImplementedError: calc not implemented for <class 'str'> and <class 'int'>
前面的示例中,被calc.register装饰的函数的参数都有清晰的类型标注,如果你不喜欢编写类型标注,也可以退而求其次,在register中约定类型,代码可以改写成下面的样子
from functools import singledispatch
from typing import Any
@singledispatch
def calc(x: Any, y: Any) -> Any:
raise NotImplementedError(f"calc not implemented for {type(x)} and {type(y)}")
@calc.register(int)
def calc_int(x, y) -> int:
print("x和y都是int")
return x + y
@calc.register(float)
def calc_float(x, y) -> float:
print("x和y都是float")
return x + y
calc(1, 2)
calc(3.4, 5.6)
对于被calc.register装饰的函数,函数名称可以是相同的,因此你也可以写成下面的样子
@calc.register(int)
def _calc(x, y) -> int:
print("x和y都是int")
return x + y
@calc.register(float)
def _calc(x, y) -> float:
print("x和y都是float")
return x + y
functools.singledispatch只能基于被装饰函数的第一个参数的类型来实现简单的派发,还不能算是真正的重载,想要根据函数的所有参数进行派发,可以使用开源库multipledispatch
pip install multipledispatch
使用示例如下
from multipledispatch import dispatch
@dispatch(int, int)
def calc(x, y) -> int:
print("x和y都是int")
return x + y
@dispatch(float, float)
def calc(x, y) -> float:
print("x和y都是float")
return x + y
@dispatch(float, int)
def calc(x, y):
print("x是float,y是int")
return x + y
calc(1, 2)
calc(3.4, 5.6)
calc(3.3, 5)
执行calc(3.3, 5) 时,会调用最后一个版本的calc的实现,singledispatch 无法实现这样的功能。
QQ交流群: 211426309