文章閱讀: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