前言及环境假设
Pycharm 2017
低版本的 Pycharm 并不支持 Type Hint,所以这里假设是比较新的版本。
Python 3.5
在现在的 Python 社区,3 越来越成为主流,好几个重量级的库/框架都不在对 2 提供支持。而 3.5 是一个比较重要的版本( 如果是新项目,一般都直接 3.5/3.6 ),所以本文使用 3.5 。
静态类型很重要
由于我并非相关专业人士,这里姑且这样称呼吧。由于 Python 是动态语言,所以 Type Hint 与 C++/Java 那样的静态类型并不是一回事,仅仅是 IDE 提示使用(都已经选择了动态语言,还要啥锤子,凑合用吧 :))。好在现在大部分人都会使用 IDE 编程(指:编写生产代码 ),所以 Type Hint 啥的,还是可堪一用的。
总之,如果把 Java/C++ 的那种比喻成火箭大炮,那Type Hint 就是打火机。左看右看,用打火机都是要比“钻木取火”要好 :)。
可能的需求
对类型的标注,我们可能有如下几个方面的需求。
一是入参。按照参数类型分,又可以分为如下几类:
- 基本内置类型,如 str,int,list
- 基本类型组合产生的复杂类型,如 list[int], dict[str, int]
- 用户自定的class,如 Person,Animal
- 函数参数,即 callable,比如回调参数:(value: str, index: int) => str
二是出参(返回值)。基本和入参的分类一致,不在赘述。
下面我们来看看,在 2 和 3 中,如何分别实现我们上面列举的需求。
Python 2 的方式:Doc String
Pycharm 对此有比较完备的支持,基本够用,唯一的缺点是繁琐。
示例如下:
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
def get_name(self):
return self._name
def get_age(self):
return self._age
class Py2TypehintTester:
def test1(self, a, b, c):
"""第一种需求
基本很容易满足
:type a: str
:param a:
:type b: str
:param b:
:type c: list
:param c:
:rtype: list
:return:
"""
return [a, b, c]
def test2(self, a, b, c):
"""第二种需求
也很容易满足,只不过需要遵循 Pycharm 规定的语法, 它才能正确解析
:type a: list[str]
:param a:
:type b: list[int]
:param b:
:type c: dict[str, int]
:param c:
:rtype: dict[str, str]
:return:
"""
for item in a:
item.upper()
def test3(self, p):
"""第三种需求
也很容易满足.
不过需要对应的 class 在本module 的空间内( 比如, 从别处 import 进来也是可以的)
:type p: Person
:param p:
:return:
"""
age = p.get_age()
def test4(self, callback):
"""第四种需求
会让你容易满足, 需要遵循特定写法
:type callback: (str, int) -> str
:param callback:
:return:
"""
s = 's'
i = 1
result = callback(s, i)
print(result) # 能正确推导为str
复制代码
这种方式的优点很明显,即: 没啥依赖,只需要遵循规定的写法就好了。
Python 3 的方式:Type Hint
这种方式是官方规定的,与上面一种方式比起来,基本上简洁许多,而且也很符合直觉,可能需要一段时间适应。
示例如下:
import typing # 需求导入这个库
# 生成复合类型, 这段代码请无视
FloatVector = typing.List[float]
StrList = typing.List[str]
MyT = typing.TypeVar('T')
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
def get_name(self):
return self._name
def get_age(self):
return self._age
class Py3TypehintTester:
def test1(self, a: str, b: str, c: str) -> list:
"""第一种需求
很简单, 形式比较简洁
:param a:
:param b:
:param c:
:return:
"""
return [a, b, c]
def test2(self, a: typing.List[str],
b: typing.List[str],
c: typing.Dict[str, int]) -> typing.Dict[str, str]:
"""第二种需求
比较简单, 需要先了解 typing 这个库
:param a:
:param b:
:param c:
:return:
"""
return {
'str': 'str1'
}
def test3(self, p: Person):
"""第三种需求
比较简单
:param p:
:return:
"""
print(p.get_age())
def test4(self, callback: typing.Callable[[str, int], str]):
"""第四种需求
稍复杂, 需要先仔细阅读下代码
不过这种需求基本出现的频率不多, 倒也不影响大局
:param callback:
:return:
"""
s = 's'
i = 1
result = callback(s, i)
print(result) # 能推导为str
复制代码
我的做法
if >= 3.5:
使用第二种,毕竟简洁许多,而且也没啥学习成本,十几分钟的事
要求也比较高: 对 IDE 版本和 Python 版本均有要求
else:
使用第一种,可以视作过渡吧,反正兼容性是最好的,哪个版本的 Pycharm 都能正确提示
复制代码
附录
- Pycharm type hint 文档:https://www.jetbrains.com/help/pycharm/type-hinting-in-pycharm.html