類型注解允許開發者顯式地聲明變量、函數參數和返回值的類型。但是加不加注解對于程序的運行沒任何影響(是非強制的,且類型注解不影響運行時行為),屬于 有了挺好,沒有也行。但是大型項目按照規范添加注解的話,對于后期開發和維護是很有幫助的,畢竟不用回退好幾層去推斷有些變量的類型。
與原生數據類型的區別
特性 | 類型注解 | 原生數據類型 |
---|---|---|
本質 | 僅為代碼中的類型提示信息,不影響代碼運行時的行為 | 實際存儲和操作數據的結構,決定了數據的操作方式和內存占用 |
作用 | 提高代碼可讀性,輔助靜態類型檢查 | 用于實際的數據存儲和處理 |
定義方式 | 在變量名后使用冒號和類型名稱進行標注 | 通過賦值語句創建具體的數據對象 |
為什么用注解
代碼補全
PyCharm 能根據類型注解提供更準確的屬性/方法建議(如知道?y: str
?后,輸入?y.
?會提示?str
?的方法)。
比如我讀取一個 json 文件并 json.load 后進行處理,像下面這種,在調用 data 的 items() 方法時,PyCharm 是沒有方法提示的(畢竟 PyCharm 沒辦法未卜先知,無法提前預測加載后的 data 是什么類型,這也能理解)。
import jsonif __name__ == '__main__':data = json.load(open("data.json", encoding="utf-8"))for key1, value1 in data.items():for key2, value2 in value1.items():print(f"{key1}\t{key2}\t{value2}")
添加注解以后,代碼補全提示就方便多了。?
import json
from typing import Dictif __name__ == '__main__':data: Dict[str, Dict[str, str]] # 提前添加對 data 的注解data = json.load(open("data.json", encoding="utf-8"))for key1, value1 in data.items():for key2, value2 in value1.items():print(f"{key1}\t{key2}\t{value2}")
類型提示
添加類型注解以后,如果賦值的數據類型和注解聲明的類型不一致的話,PyCharm 會進行提示,能夠一眼洞察類型不符的情況,提前發現錯誤,避免運行時因類型錯誤導致的?TypeError 報錯
。
維護方便
在多人協作或長期項目中,類型注解降低了理解代碼的門檻,減少因類型混淆導致的 Bug,對于后期維護很有幫助。?
可讀性提高
類型注解明確聲明了參數和返回值的預期類型,使函數接口的語義一目了然。例如?def get_user(id: int) -> User
?比未注解的版本更清晰,減少對參數類型的文字描述。
基礎類型注解
Python 內置的基本類型可以直接用于注解。
# 變量注解
name: str = "Alice"
age: int = 30
price: float = 19.99
is_active: bool = True# 函數參數和返回值注解
def greet(name: str) -> str:return f"Hello, {name}"if __name__ == '__main__':name = "Looking"print(type(name)) # <class 'str'>greet_word = greet(name)print(greet_word) # Hello, Looking
復合類型注解
from typing import List, Dict, Tuple, Set, Optional# 列表
numbers: List[int] = [1, 2, 3]# 字典
person: Dict[str, str] = {"name": "Alice", "email": "alice@example.com"}# 元組 (固定長度和類型)
point: Tuple[float, float] = (3.14, 2.71)# 集合
unique_numbers: Set[int] = {1, 2, 3}# 可選類型 (表示 middle_name 有值的時候是 str,無值的時候可以是 None)
middle_name: Optional[str] = None
函數類型注解
from typing import Callable# 基本函數注解
def add(a: int, b: int) -> int:return a + b# 帶默認值的參數
def greet(name: str, greeting: str = "Hello") -> str:return f"{greeting}, {name}"# 函數作為參數
def apply_func(func: Callable[[int, int], int], x: int, y: int) -> int:return func(x, y)if __name__ == '__main__':v1 = add(1, 2)print(v1) # 3v2 = greet("Looking")print(v2) # Hello, Lookingv3 = apply_func(add, 2, 3)print(v3) # 5
特殊類型注解
from typing import Any, Union, NoReturn, NewType# Any - 任意類型
def log(message: Any) -> None:print(message)# Union - 多個可能的類型
def square(number: Union[int, float]) -> Union[int, float]:return number ** 2# NoReturn - 函數不會正常返回
def fail() -> NoReturn:raise Exception("Something went wrong")UserId = NewType("UserId", int)
if __name__ == '__main__':log(123) # 123log("hello world") # hello worldprint(square(5)) # 25print(square(2.5)) # 6.25# UserId("hello") # 類型檢查不通過print(UserId(12345)) # 12345fail()# Traceback (most recent call last):# File "E:\lky_project\tmp_project\test.py", line 24, in <module># fail()# File "E:\lky_project\tmp_project\test.py", line 16, in fail# raise Exception("Something went wrong")# Exception: Something went wrong
類型注解別名
from typing import List, Tuple# 簡單別名
UserId = int# 復雜別名
Point = Tuple[float, float]
Vector = List[float]def distance(point1: Point, point2: Point) -> float:return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5def normalize(vector: Vector) -> Vector:length = sum(x ** 2 for x in vector) ** 0.5return [x / length for x in vector]if __name__ == '__main__':point1 = (3, 4)point2 = (0, 0)print(distance(point1, point2)) # 5.0vector = [3, 4]print(normalize(vector)) # [0.6, 0.8]
泛型類型注解
from typing import TypeVar, Generic, ListT = TypeVar('T') # 聲明無約束的類型變量class Stack(Generic[T]):def __init__(self) -> None:self.items: List[T] = []def push(self, item: T) -> None:self.items.append(item)def pop(self) -> T:return self.items.pop()if __name__ == '__main__':# 使用int_stack = Stack[int]() # 初始化實例并指定 T 的類型int_stack.push(12345)print(int_stack.items) # [12345]str_stack = Stack[str]()str_stack.push("hello")print(str_stack.items) # ['hello']
類型變量
from typing import TypeVar# 無約束的類型變量(表明 T 可以是無約束的任何類型)
T = TypeVar('T')# 有約束的類型變量
Number = TypeVar('Number', int, float, complex)def double(x: Number) -> Number:return x * 2def triple(x: T) -> T:return x * 3if __name__ == '__main__':print(double(123)) # 246print(triple("hello ")) # hello hello hello