Python 類型註解

来源:https://www.cnblogs.com/iorson/archive/2023/02/12/17114352.html
-Advertisement-
Play Games

Python類型註解僅在語法展示層面支持,對代碼的運行沒有任何影響,Python 解釋器在運行代碼的時候會忽略類型提示,Python的類型註解極大的提升了代碼可讀性,一定程度上緩解"動態語言一時爽,代碼重構火葬場"的尷尬。 ...


在Python語言發展的過程中,PEP提案發揮了巨大的作用,如PEP 3107 和 PEP 484提案,分別給我們帶來了函數註解(Function Annotations)和類型提示(Type Hints)的功能。

PEP 3107:定義了函數註解的語法,允許為函數的參數和返回值添加元數據註解。

PEP 484:按照PEP 3107函數註解的語法,從Python語法層面全面支持類型提示,類型提示可以是內置類型、內置類、抽象基類、types模塊中提供的類型和開發人員自定義的類。

另外 PEP 526, PEP 544, PEP 586, PEP 589, PEP 591 這些東西對 PEP 3107 和 PEP 484 進行了補充,比如添加了變數註釋,字面量註釋這些東西。

需要註意的是,類型提示僅有提示的作用,這裡的提示是指用戶閱讀Python代碼的時候的提示,僅在語法層面支持,對代碼的運行沒有任何影響,Python 解釋器在運行代碼的時候會忽略類型提示,也就是說,Python的類型提示僅是為了提升代碼可讀性,一定程度上緩解"動態語言一時爽,代碼重構火葬場"的尷尬。

下麵將函數註解和類型提示,統稱為類型註解。


類型註解優點

1、可以使Python擁有部分靜態語言的特性,利用類型註解可以實現一種類似類型聲明的效果,提升代碼的可讀性及後續的可維護性。

2、類型註解可以讓IDE(如pycharm)像靜態語言那樣分析我們的代碼,及時給我們相應的提示,如下圖對比:

image-20230212140705227
VS
image-20230212140817448

3、多多使用類型註解,不僅可以讓Python擁有強類型語言的嚴謹,還能保持Python作為動態類型語言的靈活性。


普通變數類型註解

在聲明變數時,變數的後面可以加一個冒號,後面再寫上變數的類型,如 int、list 等等,以此實現類型註解。

a: int = 22
b: str = "name"
c: float = 55.5
d: bool = True
e: list = [1, 2, 3]
f: set = {1, 2, 3}
g: dict = {"name": "ming", "age": 22}
h: tuple = (1, 2, 3)
i: bytes = b'world'
j: bytearray = bytearray("world")

函數參數及返回值類型

函數參數的類型聲明就是冒號+類型即可,和普通變數類型聲明沒區別。

函數返回值的類型聲明是用箭頭指向具體的類型,如果是返回值有多個,使用元組包裹即可(因為函數的多個返回值就是以元組形式返回的),需要註意的是,箭頭左右兩邊都要留有空格。

def handler(a: int, b: int) -> int:
    return a + b


def handler2(a: int, b: int, *args: int) -> int:
    return a + b + sum(args)


def handler3(a: int, b: int, *args: int, **kwargs: int) -> (int, str):
    return a + b + sum(args) + sum(kwargs.values()), ""

typing模塊

typing模塊的加入不會影響程式的運行,也不會報正式的錯誤,pycharm支持檢測基於typing註解的錯誤,不符合規定類型註解時會出現黃色警告,但不會影響程式運行。

容器類型 & 複合類型

列表、字典、元組等包含元素的複合類型,用簡單的 list,dict,tuple 不能夠明確說明內部元素的具體類型。

此外,Python本身就是動態類型的語言,如果我們強制使用某種類型,一定程度上會喪失Python作為動態語言的優勢,因此 typing 模塊提供了一種複合類型註解的語法,即一個參數即可以是類型A,也可以是類型B或者類型C

from typing import Dict, List, Set, Tuple, Union

# 字典
d: Dict[str, int] = {"a": 1, "b": 2}
d1: Dict[str, int or str] = {"a": 1, "b": "2"}  	# 使用or表示支持多個類型

# 列表
l: List[int] = [1, 2, 3]
l1: List[int or str] = [1, 2, "3"]

# 元組
t: Tuple[str, int] = ("a", 1)		# 代表了構成元組的第一個元素是 str 類型,第二個元素是 int 類型
t1: Tuple[str, ...] = ("a", "b", "c", "d", "e", "f", "g")		# 代表接受多個 str 類型的元素
t2: Tuple[str or int, ...] = ("a", "b", 2)		# 代表接受多個 str 或 int 類型的元素

# 集合
s: Set[int] = {1, 2, 3, 4}
s1: Set[Union[int, str, float]] = {1, "2", 3.333, 4}	# Union 同 or

TypedDict

TypedDict聲明一個字典類型,該類型期望它的所有實例都有一組固定的keys,其中每個key都與對應類型的值關聯。

from typing import TypedDict


class Student(TypedDict):
    name: str
    age: int
    height: float


s1: Student = {
    "name": "xiao ming",
    "age": 22,
    "height": 55.5
}

s2: Student = {
    "name": "xiao hong",
    "age": 21,
}
image-20230212162843194

可以看出,pycharm也會警告我們字典實例中缺失的key。

同時,在我們生成字典實例的時候,pycharm也會給我們key的提示。

image-20230212163042746

類型別名

類型別名是通過將類型分配給別名來定義的,類型別名可用於簡化複雜類型提示。

from typing import Union

Number = Union[int, float]

def process(v: Number) -> Number:
    return v

x: Number = 2
y: Number = 2.2
process(x)
process(22)		# 類型檢查成功,類型別名和原始類型是等價的

NewType

使用NewType輔助類來創建不同的類型

from typing import NewType

Number = NewType("Number", int)

def process(v: Number) -> Number:
    return v

x: Number = Number(22)
process(x)
process(22)     # 類型檢查異常:Expected type 'Number', got 'int' instead 
# 原因就是NewType創建的是原始類型的“子類型”

因此,類型別名 和 NewType 具體使用哪個,要視情況而定,不知道使用哪個,可以先使用類型別名。


NoReturn

當一個方法沒有返回結果時,為了註解它的返回類型,我們可以將其註解為 NoReturn。

因為Python 的函數運行結束時隱式返回 None ,這和真正的無返回值是有區別的。

from typing import NoReturn

def process() -> NoReturn:
    pass

可選類型:Optional

使用 Optional[] 表示可能為 None 的值

from typing import Optional

def handler(x: int) -> Optional[int]:
    if x % 2 == 0:
        return x

可調用對象:Callable

若一個變數類型是可調用函數,則可以用 Callable[[Arg1Type, Arg2Type], ReturnType] 實現類型提示

from typing import Optional, Callable

def handler(x: int) -> Optional[int]:
    if x % 2 == 0:
        return x

def handler2(func: Callable[[int], Optional[int]]):
    pass

handler2(handler)

字面量:Literal

指示相應的變數或函數參數只接收與提供的字面量(或多個字面量之一)等效的值,可以理解為規定了某個參數或變數的所有枚舉值。

from typing import Literal, NoReturn

Mode = Literal["r", "w"]


def process(mode: Mode) -> NoReturn:
    pass


process("s")
image-20230212162309699

可以看出,pycharm檢查出了我們輸入的值並不符合字面量規定的值,進而出現了黃色警告。

image-20230212163929671

Any

是一種特殊的類型,每種類型都視為與Any相容,同樣,Any也與所有類型相容。可以對Any類型的值執行任何操作或方法調用,並將其分配給任何變數。將Any類型的值分配給更精確的類型(more precise type)時,不會執行類型檢查,所有沒有返回類型或參數類型的函數都將隱式地預設使用Any。

使用Any,說明值是動態類型。

把所有的類型都註解為 Any 將毫無意義,因此 Any 應當儘量少使用

from typing import Any

def foo() -> Any:
    pass

抽象基類

# 在某些情況下,我們可能並不需要嚴格區分一個變數或參數到底是列表 list 類型還是元組 tuple 類型
# 可以使用一個更為泛化的類型,叫做 Sequence,其用法類似於 List
class typing.Sequence(Reversible[T_co], Collection[T_co])


# collections.abc.Iterator的泛型版本
# 註釋函數參數中的迭代類型時,推薦使用的抽象集合類型
class typing.Iterable(Generic[T_co])

def print_iterable(x: Iterable):
    for i in x:
        print(i)



# collections.abc.Mapping的泛型(generic)版本
# 註釋函數參數中的Key-Value類型時,推薦使用的抽象集合類型
class typing.Mapping(Sized, Collection[KT], Generic[VT_co])

泛型:TypeVar

先拋出問題:

假設有一個函數,要求它既能夠處理字元串,又能夠處理數字。那麼你可能很自然地想到了 Union ,如下:

from typing import Union

AddValue = Union[int, str]


def add(a: AddValue, b: AddValue) -> AddValue:
    return a + b


if __name__ == "__main__":
    print(add(1, 2))        # 類型檢查通過,輸出 3
    print(add("1", "2"))    # 類型檢查通過,輸出 12
    print(add("1", 2))      # 類型檢查通過,報錯 TypeError: can only concatenate str (not "int") to str

在類型檢查通過的情況下,我們完成並運行了這段代碼,可是代碼卻報錯了!

原因就是我們的初衷是數字和數字相加實現求和,字元串和字元串相加實現拼接,沒有考慮到字元串與數字混用的問題,從而引發錯誤。

根據以上問題,我們可以引入泛型來解決這個問題:

from typing import TypeVar

AddT = TypeVar("AddT", int, str)


def add(a: AddT, b: AddT) -> AddT:
    return a + b


if __name__ == "__main__":
    print(add(1, 2))		# 類型檢查通過,輸出 3
    print(add("1", "2"))	# 類型檢查通過,輸出 12
    print(add("1", 2))		# 類型檢查失敗,pycharm告警 Expected type 'str' (matched generic type 'AddT'), got 'int' instead

"""
通過告警,我們提前發現了混用類型的問題,避免了程式運行時發生異常的可能。
"""

泛型很巧妙地對類型進行了參數化,同時又保留了函數處理不同類型時的靈活性。


引用

1、Python 標準庫 typing 類型註解標註

2、Python類型註解,你需要知道的都在這裡了

本文來自博客園

作者:奧森iorson

轉載請註明原文鏈接:https://www.cnblogs.com/iorson/p/17114352.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 初探富文本之CRDT協同演算法 CRDT的英文全稱是Conflict-free Replicated Data Type,最初是由協同文本編輯和移動計算而發展的,現在還被用作線上聊天系統、音頻分發平臺等等。當前CRDT演算法在富文本編輯器領域的協同依舊是典型的場景,常用於作為實現文檔協同的底層演算法,支持 ...
  • 0x001 自定義參數 $extra_param = "Some extra param"; Hook::add('response_end', function($params) use ($extra_param) { Log::info('Extra: '.$extra_param); }); ...
  • 官網: https://spring.io/ 更多spring cloud zookeeper 參考 https://docs.spring.io/spring-cloud-zookeeper/docs/current/reference/html 左側菜單 向下找到 spring Cloud Zo ...
  • 這篇文章主要介紹分散式系統中的非集中式結構,以及我們經常使用的三種基於非集中式架構的解決方案:Akka集群、Redis集群和Cassandra集群。 ...
  • SpringMVC底層機制簡單實現-03 https://github.com/liyuelian/springmvc-demo.git 7.任務6-完成控制器方法獲取參數-@RequestParam 功能說明:自定義 @RequestParam 註解和方法參數名獲取參數。 當瀏覽器訪問 Handl ...
  • 最近項目 需要用到 sybase(sql anywhere)、mysql 資料庫 兩邊數據交互 。由於之前對sybase 資料庫一點不懂 踩了許多坑 特意記下: 連接 sybase 客戶端需要用到 SQL Central 17.0 (64-bit) 這個工具; 接下來我會一步一步詳細介紹: 1.連接 ...
  • 雪花演算法的原理 第一位符號位固定為0,41位時間戳,10位workld,12位序列號,位數可以有不同實現優點: 每個毫秒值包含的ID值很多,不夠可以變動位數來增加,性能佳 (依賴workld的實現)。.時間戳值在高位,中間是固定的機器碼,自增的序列在低位,整個ID是趨勢遞增的。0能夠根據業務場景數據 ...
  • 作者:翁智華 出處:https://www.cnblogs.com/wzh2010/ 背景 一天晚上10點半,下班後愉快的坐在在回家的地鐵上,心裡想著周末的生活怎麼安排。 突然電話響了起來,一看是我們的一個開發同學,頓時緊張了起來,本周的版本已經發佈過了,這時候打電話一般來說是線上出問題了。 果然, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...