类型注解允许开发者显式地声明变量、函数参数和返回值的类型。但是加不加注解对于程序的运行没任何影响(是非强制的,且类型注解不影响运行时行为),属于 有了挺好,没有也行。但是大型项目按照规范添加注解的话,对于后期开发和维护是很有帮助的,毕竟不用回退好几层去推断有些变量的类型。
与原生数据类型的区别
特性 | 类型注解 | 原生数据类型 |
---|---|---|
本质 | 仅为代码中的类型提示信息,不影响代码运行时的行为 | 实际存储和操作数据的结构,决定了数据的操作方式和内存占用 |
作用 | 提高代码可读性,辅助静态类型检查 | 用于实际的数据存储和处理 |
定义方式 | 在变量名后使用冒号和类型名称进行标注 | 通过赋值语句创建具体的数据对象 |
为什么用注解
代码补全
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