文章閱讀:Python 函式中的類型提示
如何在 Python 函式中標示類型?
詳情可參閱上方連結。
Duck Typing
作為一種動態程式語言,Python 採用「鴨子型別」的方式進行物件識別:
「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。」
例如以下的程式:
class Duck: def quack(self): print("這鴨子在呱呱叫") def feathers(self): print("這鴨子擁有白色與灰色羽毛") def getMyFeathers(self): return ["白色羽毛", "灰色羽毛"] class Person: def quack(self): print("這人正在模仿鴨子") def feathers(self): print("這人在地上拿起1根羽毛然後給其他人看") def getMyFeathers(self): return "地上的羽毛" def in_the_forest(duck): duck.quack() duck.feathers() #Start the game. donald = Duck() john = Person() in_the_forest(donald) in_the_forest(john)
以上程式碼顯示,不管你是 Duck 類型還是 Person 類型,一樣可以進入 in_the_forest
函式。
很明顯地,我們的 john 可以玩到直到「穿幫」為止。
也呼應了 Python 的設計風格:
We're all consenting adults here.
只要知道自己在做什麼,Python 可以讓寫作方式不會太過拘謹。
PEP 484 簡讀
不過 Python 中還是可以「提醒」工程師,function 中究竟該輸入什麼、會得到什麼。
這裡使用「提醒」這個字,代表著仍然能不按照規定輸入輸出(因為我們是動態語言),當然也會抱著被拆穿的風險。
使用 PEP 484 的規則設計,最大的好處是可以不須使用註解來標明,畢竟同一色系常常會打錯字或是會錯意。
而且 Python 直譯器以及絕大多數的 IDE 都會幫你檢查是否為正確的類型。
需要注意的是,typing 模組是在 Python 3.5 加入。
輸入
先來展示基本的寫法,提醒 john 不要進入 in_the_forest
函式:
def in_the_forest(duck: Duck): duck.quack() duck.feathers() whiteFeather, grayFeather = duck.getMyFeathers()
這裡使用冒號 :
後加上類型物件可以標示這個輸入的類型。
如果在其他檔案,懶得找到鴨子,也可以用字串表示有種生物叫鴨子。
def in_the_forest(duck: "Duck"):
預設項目的用法也是以此類推,比如沒有 donald,自己找隻鴨子。
def in_the_forest(duck: Duck =Duck()):
多個輸入也雷同,使用逗號隔開即可。若是很多項,則是建議換行。
多個項目中可以選擇特定的標示,這些都不是硬性規定。
下面的範例留了一個安全的位子給 john。
def in_the_forest( duck1: Duck =Duck(), duck2: Duck =Duck(), duck3: Duck =Duck(), person: Person =Person() ): Creatures = [duck1, duck2, duck3, person] for i, duck in enumerate(Creatures): duck.quack() duck.feathers() if i!=3: whiteFeather, grayFeather = duck.getMyFeathers() in_the_forest(person=john)
類型中的方法 (method) 也是一樣,不過因為第一個輸入 self 本來就是該類型,通常都不會標示。
這樣是不是清楚很多呢?
輸出
我們注意到 donald 和 john 在 getMyFeathers
函式中拿出的物件不一樣,為了瞭解類型的函式究竟會得到什麼,可以使用輸出表示的方法。
輸出表示是一個箭號形狀擺在 function 尾端的冒號 :
之前:
class Person: def getMyFeathers(self) -> str: return "地上的羽毛"
當然只會做事的函式是回傳 None,可以視情況決定要不要寫:
class Person: def quack(self) -> None: print("這人正在模仿鴨子")
若為多個,也是使用逗號分隔,這裡就不示範了。
容器物件
若是輸入或輸出的物件為容器 (container),可以使用檢索符號 [ ]
中括弧來標示。
from typing import List class Duck: def getMyFeathers(self) -> List[str, str]: return ["白色羽毛", "灰色羽毛"]
從 typing 模組匯入的類型名稱字首為大寫,注意不要與一般類型混淆了。
可呼叫物件
可呼叫 (callable) 的 function 類型和常用的 lambda 如下表示:
from typing import Callable def async_query( on_success: Callable[[int], None], on_error: Callable[[int, Exception], None] ):
第一項為「輸入類型」,第二項為「輸出類型」。
若是懶得管第一項,可以使用 Ellipsis 表示。
一個簡單的 lambda 物件如下表示:
lambda x, y: str(x+y) Callable[[int, int], str] Callable[..., str]
泛型物件
若有多重類型的物件可以適用,便可從 typing 模組匯入泛型物件。
例如無序集合:
from typing import Mapping, Set def notify_by_email(employees: Set[Employee], overrides: Mapping[str, str]):
或是有序集合:
from typing import Sequence, TypeVar T = TypeVar('T') def first(l: Sequence[T]) -> T: return l[0]
或是任何字串:
from typing import TypeVar AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: return x + y
上面的範例中,透過 TypeVar 函式可以將你的自訂類型正規化,讓直譯器不會出錯,是一個比字串更好的表達方式。
還有更多使用方式,這裡就不細數了,更多內容可以參閱官方的說明。
Comments
comments powered by Disqus