前言及環境假設
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