天天看點

RPA手把手————使用類型注解讓 Python 代碼更易讀

藝賽旗 RPA9.0全新首發免費下載下傳 點選下載下傳

http://www.i-search.com.cn/index.html?from=line1

我們知道 Python 是一種動态語言,在聲明一個變量時我們不需要顯式地聲明它的類型,例如下面的例子:a = 2

print(‘1 + a =’, 1 + a)

運作結果:1 + a = 3

這裡我們首先聲明了一個變量 a,并将其指派為了 2,然後将最後的結果列印出來,程式輸出來了正确的結果。但在這個過程中,我們沒有聲明它到底是什麼類型。但如果這時候我們将 a 變成一個字元串類型,結果會是怎樣的呢?改寫如下:a = ‘2’

print(‘1 + a =’, 1 + a)

運作結果:TypeError: unsupported operand type(s) for +: ‘int’ and ‘str’

直接報錯了,錯誤原因是我們進行了字元串類型的變量和數值類型變量的加和,兩種資料類型不同,是無法進行相加的。如果我們将上面的語句改寫成一個方法定義:def add(a):

return a + 1

這裡定義了一個方法,傳入一個參數,然後将其加 1 并傳回。如果這時候如果用下面的方式調用,傳入的參數是一個數值類型:add(2)

則可以正常輸出結果 3。但如果我們傳入的參數并不是我們期望的類型,比如傳入一個字元類型,那麼就會同樣報剛才類似的錯誤。但又由于 Python 的特性,很多情況下我們并不用去聲明它的類型,是以從方法定義上面來看,我們實際上是不知道一個方法的參數到底應該傳入什麼類型的。這樣其實就造成了很多不友善的地方,在某些情況下一些複雜的方法,如果不借助于一些額外的說明,我們是不知道參數到底是什麼類型的。是以,Python 中的類型注解就顯得比較重要了。類型注解在 Python 3.5 中,Python PEP 484 引入了類型注解(type hints),在 Python 3.6 中,PEP 526 又進一步引入了變量注解(Variable Annotations),是以上面的代碼我們改寫成如下寫法:a: int = 2

print(‘5 + a =’, 5 + a)

def add(a: int) -> int:

return a + 1

具體的文法是可以歸納為兩點:在聲明變量時,變量的後面可以加一個冒号,後面再寫上變量的類型,如 int、list 等等。在聲明方法傳回值的時候,可以在方法的後面加一個箭頭,後面加上傳回值的類型,如 int、list 等等。

在 PEP 8 中,具體的格式是這樣規定的:在聲明變量類型時,變量後方緊跟一個冒号,冒号後面跟一個空格,再跟上變量的類型。在聲明方法傳回值的時候,箭頭左邊是方法定義,箭頭右邊是傳回值的類型,箭頭左右兩邊都要留有空格。有了這樣的聲明,以後我們如果看到這個方法的定義,我們就知道傳入的參數類型了,如調用 add 方法的時候,我們就知道傳入的需要是一個數值類型的變量,而不是字元串類型,非常直覺。但值得注意的是,這種類型和變量注解實際上隻是一種類型提示,對運作實際上是沒有影響的,比如調用 add 方法的時候,我們傳入的不是 int 類型,而是一個 float 類型,它也不會報錯,也不會對參數進行類型轉換,如:add(1.5)

我們傳入的是一個 float 類型的數值 1.5,看下運作結果:2.5

可以看到,運作結果正常輸出,而且 1.5 并沒有經過強制類型轉換變成 1,否則結果會變成 2。是以,類型和變量注解隻是提供了一種提示,對于運作實際上沒有任何影響。不過有了類型注解,一些 IDE 是可以識别出來并提示的,比如 PyCharm 就可以識别出來在調用某個方法的時候參數類型不一緻,會提示 WARNING。比如上面的調用,如果在 PyCharm 中,就會有如下提示内容:Expected type ‘int’, got ‘float’ instead

This inspection detects type errors in function call expressions. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Types of function parameters can be specified in docstrings or in Python 3 function annotations.

另外也有一些庫是支援類型檢查的,比如 mypy,安裝之後,利用 mypy 即可檢查出 Python 腳本中不符合類型注解的調用情況。上面隻是用一個簡單的 int 類型做了執行個體,下面我們再看下一些相對複雜的資料結構,例如清單、元組、字典等類型怎麼樣來聲明。可想而知了,清單用 list 表示,元組用 tuple 表示,字典用 dict 來表示,那麼很自然地,在聲明的時候我們就很自然地寫成這樣了:names: list = [‘Germey’, ‘Guido’]

version: tuple = (3, 7, 4)

operations: dict = {‘show’: False, ‘sort’: True}

這麼看上去沒有問題,确實聲明為了對應的類型,但實際上并不能反映整個清單、元組的結構,比如我們隻通過類型注解是不知道 names 裡面的元素是什麼類型的,隻知道 names 是一個清單 list 類型,實際上裡面都是字元串 str 類型。我們也不知道 version 這個元組的每一個元素是什麼類型的,實際上是 int 類型。但這些資訊我們都無從得知。是以說,僅僅憑借 list、tuple 這樣的聲明是非常“弱”的,我們需要一種更強的類型聲明。這時候我們就需要借助于 typing 子產品了,它提供了非常“強“的類型支援,比如List[str]、Tuple[int, int, int] 則可以表示由 str 類型的元素組成的清單和由 int 類型的元素組成的長度為 3 的元組。是以上文的聲明寫法可以改寫成下面的樣子:from typing import List, Tuple, Dict

names: List[str] = [‘Germey’, ‘Guido’]

version: Tuple[int, int, int] = (3, 7, 4)

operations: Dict[str, bool] = {‘show’: False, ‘sort’: True}

這樣一來,變量的類型便可以非常直覺地展現出來了。目前 typing 子產品也已經被加入到 Python 标準庫中,不需要安裝第三方子產品,我們就可以直接使用了。typing下面我們再來詳細看下 typing 子產品的具體用法,這裡主要會介紹一些常用的注解類型,如 List、Tuple、Dict、Sequence 等等,了解了每個類型的具體使用方法,我們可以得心應手的對任何變量進行聲明了。在引入的時候就直接通過 typing 子產品引入就好了,例如:from typing import List, Tuple

ListList、清單,是 list 的泛型,基本等同于 list,其後緊跟一個方括号,裡面代表了構成這個清單的元素類型,如由數字構成的清單可以聲明為:var: List[int or float] = [2, 3.5]

另外還可以嵌套聲明都是可以的:var: List[List[int]] = [[1, 2], [2, 3]]

Tuple、NamedTupleTuple、元組,是 tuple 的泛型,其後緊跟一個方括号,方括号中按照順序聲明了構成本元組的元素類型,如 Tuple[X, Y] 代表了構成元組的第一個元素是 X 類型,第二個元素是 Y 類型。比如想聲明一個元組,分别代表姓名、年齡、身高,三個資料類型分别為 str、int、float,那麼可以這麼聲明:person: Tuple[str, int, float] = (‘Mike’, 22, 1.75)

同樣地也可以使用類型嵌套。NamedTuple,是 collections.namedtuple 的泛型,實際上就和 namedtuple 用法完全一緻,但個人其實并不推薦使用 NamedTuple,推薦使用 attrs 這個庫來聲明一些具有表征意義的類。Dict、Mapping、MutableMappingDict、字典,是 dict 的泛型;Mapping,映射,是 collections.abc.Mapping 的泛型。根據官方文檔,Dict 推薦用于注解傳回類型,Mapping 推薦用于注解參數。它們的使用方法都是一樣的,其後跟一個中括号,中括号内分别聲明鍵名、鍵值的類型,如:def size(rect: Mapping[str, int]) -> Dict[str, int]:

return {‘width’: rect[‘width’] + 100, ‘height’: rect[‘width’] + 100}

這裡将 Dict 用作了傳回值類型注解,将 Mapping 用作了參數類型注解。MutableMapping 則是 Mapping 對象的子類,在很多庫中也經常用 MutableMapping 來代替 Mapping。Set、AbstractSetSet、集合,是 set 的泛型;AbstractSet、是 collections.abc.Set 的泛型。根據官方文檔,Set 推薦用于注解傳回類型,AbstractSet 用于注解參數。它們的使用方法都是一樣的,其後跟一個中括号,裡面聲明集合中元素的類型,如:def describe(s: AbstractSet[int]) -> Set[int]:

return set(s)

這裡将 Set 用作了傳回值類型注解,将 AbstractSet 用作了參數類型注解。整體看下來,每個參數的類型、傳回值都進行了清晰地注解,代碼可讀性大大提高。以上便是類型注解和 typing 子產品的詳細介紹。

繼續閱讀