Python作为一门动态语言,其实并不需要重载,但是如果你愿意的话,可以显式的声明重载。
先简单介绍下什么是重载(Overload)。
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
简单的举个例子。
现在有一个get函数,接收参数v,v的类型为整数,最终get的返回值也是整数类型。
def get(v: int) -> int:
return v + 1
现在又有一个同名的get函数,接收参数v,v的类型为字符串,最终get的返回值也是字符串类型。
def get(v: str) -> str:
return v + "1"
啊这? 难道这两个函数不能合并成一个吗,对,是可以的,如下:
def get(v: Union[int, str]) -> Union[int, str]:
if isinstance(v, int):
return v + 1
elif isinstance(v, str):
return v + "1"
对于编辑器而言,get的参数v
可能是int
,也可能是str
,同理返回值可能是int
也可能是str
。这里就出现歧义,现在我作为get
的调用者,我现在传递一个int
类型的参数,那get
的返回值是int
呢,还是str
呢?
可以看到,无论我传入的参数是int
还是str
,返回值全都是Union[int, str]
。
作为方法的开发者,我们固然可以在get
函数的注释中,描述其函数的功能,例如:
参数v
接收int
和str
类型的数据,传入int
则返回值是int
类型,传入str
则返回值是str
类型。
当然,作为调用者也可以在拿到get
的返回值后,使用isinstance
进行类型的判断。
这里,我想介绍的方法是重载装饰器,@overload
https://docs.python.org/zh-cn/3/library/typing.html?highlight=typing#typing.overload
overload
是标准库typing
中的一个装饰器,使用方法很简单。
from typing import overload
@overload
def get(v: int) -> int:
...
@overload
def get(v: str) -> str:
...
def get(v):
if isinstance(v, int):
return v + 1
elif isinstance(v, str):
return v + "1"
可以看到,编辑器会根据我们传入的值,提示我们函数返回值的类型。
现在你可能会认为,功能是实现了,但是也增加了很多无用的代码。
这个确实,所以如果你想以这样的方式进行更加具体的类型标注,我建议你将类型标注的代码写入存根文件中。
存根文件指的是,后缀为pyi
结尾的文件,它的目的就是用于类型标注的。
所以我们可以优化上方代码。
a.py
代码的功能主体
def get(v):
if isinstance(v, int):
return v + 1
elif isinstance(v, str):
return v + "1"
else:
raise TypeError
return v
定义同名存根文件
a.pyi
from typing import overload
@overload
def get(v: int) -> int:
...
@overload
def get(v: str) -> str:
...
这样就可以将类型注解与功能进行分开了。
其实我们使用Python,看重的就是它的灵活性,而我强制的加上了类型注解,让它变得不那么“动态”了。在写代码时加上类型注解,确实会影响我们的开发效率,代码也不再那么Python,但是这也确确实实帮助到了我们代码的调用者,让调用者提升了效率。
如果你想开发一个自己的Python第三方库,我真心的建议你加上类型注解,无论是为了代码的调用者,还是为了日后的维护。