使用動態語言一時爽,程式碼重構火葬場。相信你一定聽過這句話,和單元測試一樣,雖然寫程式碼的時候花費你少量的時間,但是從長遠來看,這是非常值得的。本文分享如何更好的理解和使用 Python 的類型提示。
類型提示(自PEP 3107 開始引入)用於向變數、參數、函數參數以及它們的返回值、類別屬性和方法添加類型。
Python 的變數類型是動態的,可以在運行時修改,為程式碼添加類型提示,僅在語法層面支持,對程式碼的運行沒有任何影響,Python 解釋器在運行程式碼的時候會忽略類型提示。
因此型別提示一個直覺的作用就是提升程式碼的可讀性,方便呼叫者傳入/傳出適當型別的參數,便於程式碼重構。
Python 內建的基本類型可以直接用於類型提示:
變數的類型提示範例:
a: int = 3 b: float = 2.4 c: bool = True d: list = ["A", "B", "C"] e: dict = {"x": "y"} f: set = {"a", "b", "c"} g: tuple = ("name", "age", "job")
函數的類型提示:
def add_numbers(x: type_x, y: type_y, z: type_z= 100) -> type_return: return x + y + z
這裡的type_x , type_y , type_z , type_return 可以是內建的基本類型,也可以是自訂類型。
類別的型別提示:
class Person: first_name: str = "John" last_name: str = "Does" age: int = 31
假如有這樣一段程式碼:
x: int = 2 x = 3.5
用Python 解釋器執行是不會有任何錯誤的:
借助於mypy 就可以,先pip install mypy 安裝一下,然後mypy script.py 即可:
更多mypy 相關可以參考前文mypy 這個工具,讓Python的類型提示變得非常實用。
如果解釋器沒有強制執行型別提示,為什麼還要寫型別提示呢?確實,類型提示不會改變程式碼的運作方式:Python 本質上是動態類型的,這一點不太可能會改變。但是,從開發人員經驗的角度來看,類型提示有許多好處。
(1)、使用類型提示,尤其是在函數中,透過類型提示來明確參數類型和所產生結果的類型,非常便於閱讀和理解。
(2)、類型提示消除了認知開銷,並使程式碼更易於閱讀和除錯。考慮到輸入和輸出的類型,你可以輕鬆推斷物件以及它們如何調用。
(3)、類型提示可改善程式碼編輯體驗。 IDE 可以依靠類型偵測來靜態分析你的程式碼並幫助偵測潛在的錯誤(例如,傳遞錯誤類型的參數、呼叫錯誤的方法等)。另外,也可以根據類型提示為每個變數提供自動補全。
IDE 的類型檢查
#IDE 的類型檢查
IDE 類型檢查後的自動補全
假如你需要列表list 內部是float 的類型提示,這樣做是不行的:
def my_dummy_function(l: list[float]): return sum(l)
標準函式庫typing 考慮到了這個問題,你可以這樣:
from typing import List def my_dummy_function(vector: List[float]): return sum(vector)
假如要提示這樣的類型:
my_dict = {"name": "Somenzz", "job": "engineer"}
借助於Dict,你可以這樣定義類型:
from typing import Dict my_dict_type = Dict[str, str] my_dict: my_dict_type = {"name": "Somenzz", "job": "engineer"}
假如你需要提示這樣的類型,那該怎麼辦?
d = {"name": "Somenzz", "interests": ["chess", "tennis"]}
借助TypedDict ,你可以這樣:
TypedDict
從Python 3.10 開始,Union 被替換為| 這意味著Union[X, Y] 現在等價於X | Y。
Union[X, Y](或 X | Y)表示 X 或 Y。
假設你的函數需要從快取目錄中讀取檔案並載入 Torch 模型。此快取目錄位置可以是字串值(例如/home/cache ),也可以是Pathlib 庫的Path 對象,在這種情況下,程式碼如下:
def load_model(filename: str, cache_folder: Union[str, Path]): if isinstance(cache_folder, Path): cache_folder = str(cache_folder) model_path = os.join(filename, cache_folder) model = torch.load(model_path) return model##8、 Callable 用法當你需要傳入一個函數當參數的時候,這個參數的型別提示可以為Callable。
from typing import Callable def sum_numbers(x: int, y: int) -> int: return x + y def foo(x: int, y: int, func: Callable) -> int: output = func(x, y) return output foo(1, 2, sum_numbers)你也可以給這樣的函數參數指定參數列表,真的很強大:語法:
Callable[[input_type_1, ...], return_type]範例:
def foo(x: int, y: int, func: Callable[[int, int], int]) -> int: output = func(x, y) return output
当你传入的参数可以为任何类型的时候,就可以使用 Any
def bar(input: Any): ...
如果你的函数使用可选参数,具有默认值,那么你可以使用类型模块中的 Optional 类型。
from typing import Optional def foo(format_layout: Optional[bool] = True): ...
Sequence 类型的对象是可以被索引的任何东西:列表、元组、字符串、对象列表、元组列表的元组等。
from typing import Sequence def print_sequence_elements(sequence: Sequence[str]): for i, s in enumerate(s): print(f"item {i}: {s}"
Tuple 类型的工作方式与 List 类型略有不同,Tuple 需要指定每一个位置的类型:
from typing import Tuple t: Tuple[int, int, int] = (1, 2, 3)
如果你不关心元组中每个元素的类型,你可以继续使用内置类型 tuple。
t: tuple = (1, 2, 3, ["cat", "dog"], {"name": "John"})
类型提示在代码之上带来了额外的抽象层:它们有助于记录代码,澄清关于输入/输出的假设,并防止在顶部执行静态代码分析 (mypy) 时出现的隐蔽和错误。
以上是Python中型別提示的最佳實踐的詳細內容。更多資訊請關注PHP中文網其他相關文章!