首頁 > 軟體

Python中typing模組的具體使用

2022-05-16 19:00:23

typing庫

一、 簡介

Python是一門弱型別的語言,很多時候我們可能不清楚函數引數型別或者返回值型別,很有可能導致一些型別沒有指定方法,在寫完程式碼一段時間後回過頭看程式碼,很可能忘記了自己寫的函數需要傳什麼引數,返回什麼型別的結果,就不得不去閱讀程式碼的具體內容,降低了閱讀的速度,typing模組可以很好的解決這個問題

Python 執行時並不強制標註函數和變數型別。型別標註可被用於第三方工具,比如型別檢查器、整合式開發環境、靜態檢查器等

typing的主要作用有:

  • 型別檢查,防止執行時出現引數、返回值型別不符
  • 作為開發檔案附加說明,方便使用者呼叫時傳入和返回引數型別
  • 模組加入不會影響程式的執行不會報正式的錯誤,pycharm支援typing檢查錯誤時會出現黃色警告

官方檔案:【https://docs.python.org/zh-cn/3/library/typing.html

語法:

def 函數名(引數: 資料型別) -> 返回值型別:
    pass

變數名: 資料型別 = 值

二、 別名

1、 型別別名

要定義一個型別別名,可以將一個型別賦給別名。型別別名可用於簡化複雜型別簽名,同時型別別名適用於簡化複雜的型別簽名

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import Sequence

ConnectionOptions = dict[str, int]  # 表示字典中的鍵為字串型別,值為整型
Address = tuple[str, int, ...]  # 表示元組的第一個資料為字串,第二個資料為整型,裡面只能儲存兩個資料,有省略號表示裡面可以新增n個整型資料
Server = tuple[Address, ConnectionOptions]


def broadcast_message(message: str,
                      servers: Sequence[Server]  # 表示一個序列物件裡面儲存了[tuple[tuple[str, int], dict[str, int]]]
                      ) -> None:  # 返回值為空
    ...

broadcast_message("a", [(("a", 1, 2), {"a": 1})])

2、 NewType

使用NewType輔助函數建立不同的型別,靜態型別檢查器會將新型別視為它是原始資料的子類,相當於C++裡面的`typedef

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import NewType

UserId = NewType('UserId', int)  # 其不會建立一個新的類或引入其他記憶體,只是做一個約束作用


def name_by_id(user_id: UserId) -> str:
    ...


name_by_id(42)  # Fails type check
name_by_id(UserId(42))  # OK

num = UserId(5) + 1  # type: int,可以進行對應資料型別的操作

同時,可以巢狀建立,即可以基於NewType建立NewType

3、 可呼叫物件

Callable[[Arg1Type, Arg2Type], ReturnType]

如,實現一個互斥鎖的裝飾器

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from collections.abc import Callable  # 注意要使用Concatenate和ParamSpec就必須使用這個模組裡面的Callable
from threading import Lock
from typing import TypeVar
from pip._vendor.typing_extensions import Concatenate, ParamSpec  # 匯入typing的擴充套件

P = ParamSpec('P')  # 裡面有args和kwargs引數
R = TypeVar('R')  # 自定義資料型別

my_lock = Lock()  # 建立一個互斥鎖


def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]:
    '''一個提供互斥鎖,使得執行緒安全的裝飾器'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> R:
        return f(my_lock, *args, **kwargs)
    return inner


@with_lock
def sum_threadsafe(lock: Lock, numbers: list[float]) -> float:
    '''Add a list of numbers together in a thread-safe manner.'''
    with lock:
        return sum(numbers)


# We don't need to pass in the lock ourselves thanks to the decorator.
print(sum_threadsafe([1.1, 2.2, 3.3]))

無需指定呼叫簽名,用省略號字面量替換型別提示裡的參數列: Callable[..., ReturnType],就可以宣告可調物件的返回型別

與 Callable 和 ParamSpec 一起使用,對一個高階可呼叫物件進行型別註釋,該物件可以增加、刪除或轉換另一個可呼叫物件的引數。 使用形式為Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]。 Concatenate 目前只在作為 Callable 的第一個引數時有效。Concatenate 的最後一個引數必須是一個 ParamSpec

三、 泛型支援

typing模快最基本的支援有Any ,Tuple,Callable,TypeVar 和 Generic型別組成

1、集合型別

from typing import (
    List,  # list的泛型版本。用於註釋返回型別。要註釋引數,最好使用抽象集合型別,如Sequence或Iterable
    Set,  # set的泛型版本
    Dict  # dict 的泛型版本。對標註返回型別比較有用。如果要標註引數的話,使用如 Mapping 的抽象容器型別是更好的選擇
    )

2、 抽象基礎類別

from typing import (
    Mapping,  # 要註釋函數引數中的Key-Value型別時,推薦使用的抽象集合型別
    Sequence,  # 要註釋函數引數中的序列例如列表型別時,推薦使用的抽象集合型別
    Iterable  # 要註釋函數引數中的迭代型別時,推薦使用的抽象集合型別
    )

3、 泛型

TypeVar:其就像C++裡面的template一樣

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Sequence,
    TypeVar  # 限制多個變數為同一個資料型別
)

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

AnyStr

AnyStr是一個字串和位元組型別的特殊型別變數AnyStr = TypeVar('AnyStr', str, bytes),它用於可以接受任何型別的字串而不允許不同型別的字串混合的函數

4、 Any

特殊型別,表明型別沒有任何限制

  • 每一個型別都對 Any 相容
  • Any 對每一個型別都相容

Any 是一種特殊的型別。靜態型別檢查器將所有型別視為與Any相容,反之亦然, Any也與所有型別相相容。

這意味著可對型別為 Any 的值執行任何操作或者方法呼叫並將其賦值給任意變數

如下所示,將 Any 型別的值賦值給另一個更具體的型別時,Python不會執行型別檢查。例如,當把 a 賦值給 s 時,即使 s 被宣告為 str型別,在執行時接收到的是 int 值,靜態型別檢查器也不會報錯

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Any,
    NoReturn,  # 表示函數沒有返回值
)

def test(s: Any) -> NoReturn:
    s.item()  # 不會檢測s裡面是否有item()屬性

def test_(s: object) -> NoReturn:
    s.item()  # 會檢測s裡面是否有item屬性

當引數無型別是,預設為Any型別

5、 特殊形式

5.1 Type

一個註解為 C 的變數可以接受一個型別為 C 的值。相對地,一個註解為 Type[C] 的變數可以接受本身為類的值 。 更精確地說它接受 C的 類物件

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : test.py
# @time : 2022/5/13 16:54
from typing import (
    Type,
)


class User:
    ...


class BasicUser(User):
    ...

# Accepts User, BasicUser, ...

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


print(make_new_user(User))

5.2 Union

聯合型別;Union[X, Y]意味著:要麼是 X,要麼就是 Y。定義一個聯合型別,需要注意的有:

  • 引數必須是型別,而且必須至少有一個引數。
  • 能繼承或者範例化一個聯合型別。
  • Union[X, Y]不能寫成 Union[X][Y] 。
  • 可以使用 Optional[X] 作為Union[X, None]的縮寫- 聯合型別的聯合型別會被展開打平
  • 僅有一個引數的聯合型別會坍縮成引數自身,比如:
Union[int] == int  # The constructor actually returns int

多餘的引數會被跳過,比如:

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

在比較聯合型別的時候,引數順序會被忽略,比如:

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

5.3 Optional

可選型別,Optional[X] 等價於Union[X, None]

5.4 Tuple

元組型別,Tuple[X, Y] 標註了一個二元組型別,其第一個元素的型別為 X 且第二個元素的型別為Y。空元組的型別可寫作 Tuple[()]

為表達一個同型別元素的變長元組,使用省略號字面量,如Tuple[int, ...]。單獨的一個 Tuple 等價於 Tuple[Any, ...],進而等價於tuple

範例: Tuple[int, float, str]表示一個由整數、浮點數和字串組成的三元組

5.5 Callable

可呼叫型別;Callable[[int], str]是一個函數,接受一個 int 引數,返回一個str。下標值的語法必須恰為兩個值:參數列和返回型別。參數列必須是一個型別和省略號組成的列表;返回值必須是單一一個型別

不存在語法來表示可選的或關鍵詞引數,這類函數型別罕見用於回撥函數。Callable[..., ReturnType](使用字面省略號)能被用於提示一個可呼叫物件,接受任意數量的引數並且返回 ReturnType。單獨的 Callable 等價於Callable[..., Any],並且進而等價於 collections.abc.Callable

更多語法,請到官方檢視!!!

到此這篇關於Python中typing模組的具體使用的文章就介紹到這了,更多相關Python typing模組內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com