Python中typing模塊詳解

Python中typing模塊詳解

簡介

Python是一門動態(tài)語言,很多時(shí)候我們可能不清楚函數(shù)參數(shù)類型或者返回值類型,很有可能導(dǎo)致一些類型沒有指定方法,在寫完代碼一段時(shí)間后回過頭看代碼,很可能忘記了自己寫的函數(shù)需要傳什么參數(shù),返回什么類型的結(jié)果法牲,就不得不去閱讀代碼的具體內(nèi)容,降低了閱讀的速度琼掠,typing模塊可以很好的解決這個(gè)問題拒垃。

Python 運(yùn)行時(shí)并不強(qiáng)制標(biāo)注函數(shù)和變量類型。類型標(biāo)注可被用于第三方工具瓷蛙,比如類型檢查器悼瓮、集成開發(fā)環(huán)境、靜態(tài)檢查器等艰猬。

自python3.5開始横堡,PEP484為python引入了類型注解(type hints),typing的主要作用有:

  1. 類型檢查冠桃,防止運(yùn)行時(shí)出現(xiàn)參數(shù)命贴、返回值類型不符。

  2. 作為開發(fā)文檔附加說明食听,方便使用者調(diào)用時(shí)傳入和返回參數(shù)類型胸蛛。

  3. 模塊加入不會影響程序的運(yùn)行不會報(bào)正式的錯誤,pycharm支持typing檢查錯誤時(shí)會出現(xiàn)黃色警告樱报。

別名和NewType

1. 類型別名

要定義一個(gè)類型別名胚泌,可以將一個(gè)類型賦給別名。類型別名可用于簡化復(fù)雜類型簽名肃弟,在下面示例中,Vectorlist[float] 將被視為可互換的同義詞:


Vector = list[float] 

def scale(scalar: float, vector: Vector) -> Vector: 

    return [scalar * num for num in vector] 

# typechecks; a list of floats qualifies as a Vector.

new_vector = scale(2.0, [1.0, -4.2, 5.4])

請注意零蓉,None 作為類型提示是一種特殊情況笤受,并且由 type(None) 取代,這是因?yàn)?code>None是一個(gè)存在于解釋器中的單例對象敌蜂。

2. NewType

使用 NewType輔助函數(shù)創(chuàng)建不同的類型箩兽,靜態(tài)類型檢查器會將新類型視為它是原始類型的子類。


from typing import NewType 

UserId = NewType('UserId', int) 

def get_user_name(user_id: UserId) -> str:

    ... 

# typechecks 

user_a = get_user_name(UserId(42351)) 

# does not typecheck; an int is not a UserId 

user_b = get_user_name(-1) 

仍然可以對 UserId 類型的變量執(zhí)行所有的 int 支持的操作章喉,但結(jié)果將始終為 int 類型汗贫。這可以讓你在需要 int 的地方傳入 UserId身坐,但會阻止你以無效的方式無意中創(chuàng)建 UserId:


# 'output' is of type 'int', not 'UserId' 

output = UserId(23413) + UserId(54341) 

需要注意,這些檢查僅通過靜態(tài)類型檢查程序來強(qiáng)制落包。NewType返回的是一個(gè)函數(shù)該函數(shù)立即返回傳遞它的任意值這就意味著UserId(1234)并不會創(chuàng)建一個(gè)新的類或引入任何超出常規(guī)函數(shù)調(diào)用的開銷部蛇。
因此,運(yùn)行過程中same_value is Newtype("TypeName", Base)(same_value) 始終為True咐蝇。
但是涯鲁,可以基于 NewType 創(chuàng)建 NewType


使用類型別名聲明兩種類型彼此 等效 有序。Alias = Original 將使靜態(tài)類型檢查對待所有情況下 Alias 完全等同于 Original抹腿。當(dāng)您想簡化復(fù)雜類型簽名時(shí),這很有用旭寿。
相反警绩,NewType 聲明一種類型是另一種類型的子類型。Derived = NewType('Derived', Original) 將使靜態(tài)類型檢查器將 Derived 當(dāng)作 Original子類 盅称,這意味著 Original 類型的值不能用于 Derived 類型的值需要的地方肩祥。當(dāng)您想以最小的運(yùn)行時(shí)間成本防止邏輯錯誤時(shí),這非常有用微渠。

常用類型

typing模塊最基本的支持由 Any搭幻,TupleCallable逞盆,TypeVarGeneric類型組成檀蹋。

1. 泛型集合類型

class typing.List(list, MutableSequence[T])

list的泛型版本。用于注釋返回類型云芦。要注釋參數(shù)俯逾,最好使用抽象集合類型,如Sequence或Iterable舅逸。示例:


T = TypeVar('T', int, float) 

def vec2(x: T, y: T) -> List[T]:

    return [x, y] 

def keep_positives(vector: Sequence[T]) -> List[T]:

    return [item for item in vector if item > 0] 

class typing.Dict(dict, MutableMapping[KT, VT])

dict 的泛型版本桌肴。對標(biāo)注返回類型比較有用。如果要標(biāo)注參數(shù)的話琉历,使用如 Mapping 的抽象容器類型是更好的選擇坠七。示例:


def count_words(text: str) -> Dict[str, int]: 

    ... 

類似的類型還有

class typing.Set(set, MutableSet[T])

2. 抽象基類

class typing.Iterable(Generic[T_co])

要注釋函數(shù)參數(shù)中的迭代類型時(shí),推薦使用的抽象集合類型旗笔。

class typing.Sequence(Reversible[T_co], Collection[T_co])

要注釋函數(shù)參數(shù)中的序列例如列表類型時(shí)彪置,推薦使用的抽象集合類型。

class typing.Mapping(Sized, Collection[KT], Generic[VT_co])

要注釋函數(shù)參數(shù)中的Key-Value類型時(shí)蝇恶,推薦使用的抽象集合類型拳魁。

3. 泛型

class typing.TypeVar

類型變量。

需要注意的是 TypeVar不是一個(gè)類使用 isinstance(x, T) 會在運(yùn)行時(shí)拋出 TypeError 異常撮弧。一般地說潘懊, isinstance()issubclass()不應(yīng)該和類型變量一起使用姚糊。示例:


T = TypeVar('T')  # Can be anything 

A = TypeVar('A', str, bytes)  # Must be str or bytes 

def repeat(x: T, n: int) -> Sequence[T]: 

"""Return a list containing n references to x."""

    return [x]*n 

def longest(x: A, y: A) -> A: 

"""Return the longest of two strings."""

    return x if len(x) >= len(y) else y 

typing.AnyStr

AnyStr是一個(gè)字符串和字節(jié)類型的特殊類型變量AnyStr = TypeVar('AnyStr', str, bytes),它用于可以接受任何類型的字符串而不允許不同類型的字符串混合的函數(shù)授舟。


def concat(a: AnyStr, b: AnyStr) -> AnyStr: 

   return a + b 

concat(u"foo", u"bar") # Ok, output has type 'unicode'
concat(b"foo", b"bar") # Ok, output has type 'bytes'
concat(u"foo", b"bar") # Error, cannot mix unicode and bytes

class typing.Generic

泛型的抽象基類型救恨,泛型類型通常通過繼承具有一個(gè)或多個(gè)類型變量的該類的實(shí)例來聲明。

  • 泛型類型可以有任意數(shù)量的類型變量岂却,并且類型變量可能會受到限制忿薇。

  • 每個(gè)參數(shù)的類型變量必須是不同的。


X = TypeVar('X') 

Y = TypeVar('Y') 

class Mapping(Generic[KT, VT]): 

   def __getitem__(self, key: KT) -> VT: ... 

   def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:

   try:

       return mapping[key] 

   except KeyError: 

       return default

  • 可以對 Generic使用多重繼承躏哩。

from collections.abc import Sized 

from typing import TypeVar, Generic 

T = TypeVar('T') 

class LinkedList(Sized, Generic[T]): ... 

  • 從泛型類繼承時(shí)署浩,某些類型變量可能是固定的。

from collections.abc import Mapping 

from typing import TypeVar 

T = TypeVar('T') 

class MyDict(Mapping[str, T]): ... 

4. 特殊類型

typing.Any

特殊類型扫尺,表明類型沒有任何限制筋栋。

  • 每一個(gè)類型都對 Any 兼容。

  • Any 對每一個(gè)類型都兼容正驻。

Any 是一種特殊的類型弊攘。靜態(tài)類型檢查器將所有類型視為與Any兼容,反之亦然姑曙, Any也與所有類型相兼容襟交。

這意味著可對類型為 Any 的值執(zhí)行任何操作或者方法調(diào)用并將其賦值給任意變量。如下所示伤靠,將 Any 類型的值賦值給另一個(gè)更具體的類型時(shí)捣域,Python不會執(zhí)行類型檢查。例如宴合,當(dāng)把 a 賦值給 s 時(shí)焕梅,即使 s 被聲明為 str類型,在運(yùn)行時(shí)接收到的是 int 值卦洽,靜態(tài)類型檢查器也不會報(bào)錯


from typing import Any 

a = None  # type: Any 

a = []  # OK 

a = 2 # OK 

s = ''  # type: str 

s = a # OK 

def foo(item: Any) -> int: 

   # Typechecks; 'item' could be any type, 

   # and that type might have a 'bar' method

   item.bar() 

   ... 

所有返回值無類型或形參無類型的函數(shù)將隱式地默認(rèn)使用Any 類型贞言,如下所示2種寫法等效。


def legacy_parser(text): 

    ... 

    return data 

# A static type checker will treat the above

# as having the same signature as: 

def legacy_parser(text: Any) -> Any:

    ... 

    return data 

Anyobject 的行為對比阀蒂。與Any相似该窗,所有的類型都是object 的子類型。然而不同于 Any蚤霞,反之并不成立:object 不是 其他所有類型的子類型酗失。

這意味著當(dāng)一個(gè)值的類型是object 的時(shí)候,類型檢查器會拒絕對它的幾乎所有的操作争便。把它賦值給一個(gè)指定了類型的變量(或者當(dāng)作返回值)是一個(gè)類型錯誤。比如說,下述代碼hash_a會被IDE標(biāo)注不能從object找到magic的引用錯誤断医,而hash_b則不會:


def hash_a(item: object) -> int: 

   # Fails; an object does not have a 'magic' method.  

   item.magic() 

def hash_b(item: Any) -> int: 

   # Typechecks 

   item.magic() 

# Typechecks, since ints and strs are subclasses of objecthash_a(42) 

hash_a("foo")  

# Typechecks, since Any is compatible with all typeshash_b(42) 

hash_b("foo") 

typing.NoReturn

標(biāo)記一個(gè)函數(shù)沒有返回值的特殊類型滞乙。


from typing import NoReturn 

def stop() -> NoReturn: 

    raise RuntimeError

5. 特殊形式

class typing.Type(Generic[CT_co])

一個(gè)注解為 C 的變量可以接受一個(gè)類型為 C 的值奏纪。相對地,一個(gè)注解為 Type[C] 的變量可以接受本身為類的值 斩启。 更精確地說它接受 C的 類對象 序调,例如:


a = 3 # Has type 'int' 

b = int # Has type 'Type[int]' 

c = type(a) # Also has type 'Type[int]' 

注意Type[C] 是協(xié)變的:


class User: ... 

class BasicUser(User): ... 

class ProUser(User): ... 

class TeamUser(User): ... 

 # Accepts User, BasicUser, ProUser, TeamUser, ... 

def make_new_user(user_class: Type[User]) -> User: 

    # ... 

    return user_class()

typing.Tuple

元組類型,Tuple[X, Y] 標(biāo)注了一個(gè)二元組類型,其第一個(gè)元素的類型為 X 且第二個(gè)元素的類型為Y兔簇》⒕睿空元組的類型可寫作 Tuple[()]

為表達(dá)一個(gè)同類型元素的變長元組,使用省略號字面量垄琐,如Tuple[int, ...]边酒。單獨(dú)的一個(gè) Tuple 等價(jià)于 Tuple[Any, ...],進(jìn)而等價(jià)于tuple 狸窘。

示例: Tuple[int, float, str]表示一個(gè)由整數(shù)墩朦、浮點(diǎn)數(shù)和字符串組成的三元組。

typing.Union

聯(lián)合類型翻擒;Union[X, Y]意味著:要么是 X氓涣,要么就是 Y。定義一個(gè)聯(lián)合類型陋气,需要注意的有:

  • 參數(shù)必須是類型劳吠,而且必須至少有一個(gè)參數(shù)。

  • 能繼承或者實(shí)例化一個(gè)聯(lián)合類型巩趁。

  • Union[X, Y]不能寫成 Union[X][Y] 痒玩。

  • 可以使用 Optional[X] 作為Union[X, None]的縮寫- 聯(lián)合類型的聯(lián)合類型會被展開打平,比如


Union[Union[int, str], float] == Union[int, str, float] 

  • 僅有一個(gè)參數(shù)的聯(lián)合類型會坍縮成參數(shù)自身晶渠,比如:

Union[int] == int # The constructor actually returns int

  • 多余的參數(shù)會被跳過凰荚,比如:

Union[int, str, int] == Union[int, str]

  • 在比較聯(lián)合類型的時(shí)候,參數(shù)順序會被忽略褒脯,比如:

Union[int, str] == Union[str, int]

typing.Optional

可選類型便瑟。Optional[X] 等價(jià)于Union[X, None]


def sqrt(x: Union[int, float])->Optional[float]: 

    if x >= 0: 

        return math.sqrt(x) 

typing.Callable

可調(diào)用類型番川;Callable[[int], str]是一個(gè)函數(shù)到涂,接受一個(gè) int 參數(shù),返回一個(gè)str颁督。下標(biāo)值的語法必須恰為兩個(gè)值:參數(shù)列表和返回類型践啄。參數(shù)列表必須是一個(gè)類型和省略號組成的列表;返回值必須是單一一個(gè)類型沉御。

不存在語法來表示可選的或關(guān)鍵詞參數(shù)屿讽,這類函數(shù)類型罕見用于回調(diào)函數(shù)。Callable[..., ReturnType](使用字面省略號)能被用于提示一個(gè)可調(diào)用對象,接受任意數(shù)量的參數(shù)并且返回 ReturnType伐谈。單獨(dú)的 Callable 等價(jià)于Callable[..., Any]烂完,并且進(jìn)而等價(jià)于 collections.abc.Callable

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诵棵,一起剝皮案震驚了整個(gè)濱河市抠蚣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌履澳,老刑警劉巖嘶窄,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異距贷,居然都是意外死亡柄冲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門储耐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羊初,“玉大人,你說我怎么就攤上這事什湘〕ぴ蓿” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵闽撤,是天一觀的道長得哆。 經(jīng)常有香客問我,道長哟旗,這世上最難降的妖魔是什么贩据? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮闸餐,結(jié)果婚禮上饱亮,老公的妹妹穿的比我還像新娘。我一直安慰自己舍沙,他們只是感情好近上,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拂铡,像睡著了一般壹无。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上感帅,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天斗锭,我揣著相機(jī)與錄音,去河邊找鬼失球。 笑死岖是,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播豺撑,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼作箍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了前硫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤荧止,失蹤者是張志新(化名)和其女友劉穎屹电,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跃巡,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡危号,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了素邪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片外莲。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖兔朦,靈堂內(nèi)的尸體忽然破棺而出偷线,到底是詐尸還是另有隱情,我是刑警寧澤沽甥,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布声邦,位于F島的核電站,受9級特大地震影響摆舟,放射性物質(zhì)發(fā)生泄漏亥曹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一恨诱、第九天 我趴在偏房一處隱蔽的房頂上張望媳瞪。 院中可真熱鬧,春花似錦照宝、人聲如沸蛇受。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽龙巨。三九已至,卻和暖如春熊响,著一層夾襖步出監(jiān)牢的瞬間旨别,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工汗茄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秸弛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像递览,于是被迫代替她去往敵國和親叼屠。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345