Python 类型注解
类型注解(Type Hints)是 Python 3.5 引入的功能,可以为变量、函数参数和返回值添加类型信息。
为什么要使用类型注解?
- 提高代码可读性:明确函数的输入输出类型
- 静态类型检查:配合 mypy 等工具进行类型检查
- IDE 支持:提供更好的代码补全和错误提示
- 文档作用:作为代码的文档说明
基本类型注解
变量注解
# Python 3.6+
name: str = "张三"
age: int = 20
price: float = 19.99
is_active: bool = True
# 列表
numbers: list[int] = [1, 2, 3] # Python 3.9+
# 或 (Python 3.5-3.8)
from typing import List
numbers: List[int] = [1, 2, 3]
# 字典
person: dict[str, int] = {"age": 20} # Python 3.9+
# 或
from typing import Dict
person: Dict[str, int] = {"age": 20}
函数注解
def greet(name: str) -> str:
return f"Hello, {name}!"
def add(a: int, b: int) -> int:
return a + b
# 无返回值
def print_hello(name: str) -> None:
print(f"Hello, {name}")
类属性注解
class Person:
name: str
age: int
def __init__(self, name: str, age: int):
self.name = name
self.age = age
高级类型
Union 类型
from typing import Union
# 多种类型之一
def process(value: Union[int, str]) -> str:
return str(value)
# Python 3.10+ 可以使用 | 语法
def process(value: int | str) -> str:
return str(value)
Optional 类型
from typing import Optional
# 可能为 None
def find_user(user_id: int) -> Optional[str]:
if user_id > 0:
return "张三"
return None
# Python 3.10+ 等效写法
def find_user(user_id: int) -> str | None:
if user_id > 0:
return "张三"
return None
Any 类型
from typing import Any
# 任意类型
def log_value(value: Any) -> None:
print(value)
# Any 表示无约束,任何类型都可以
def flexible_func(x: Any) -> Any:
return x
Callable 类型
from typing import Callable
# 可调用对象(函数)
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
def add(a: int, b: int) -> int:
return a + b
result = apply(add, 3, 5) # 8
# 无返回值的回调
def on_click(callback: Callable[[], None]) -> None:
callback()
Tuple 类型
from typing import Tuple
# 固定长度的元组
point: Tuple[int, int] = (10, 20)
person: Tuple[str, int, str] = ("张三", 20, "北京")
# 可变长度的元组
numbers: Tuple[int, ...] = (1, 2, 3, 4, 5)
Literal 类型
from typing import Literal
# 限制特定值
def move(direction: Literal["left", "right", "up", "down"]) -> None:
print(f"Moving {direction}")
# 限制特定值和类型
def set_status(status: Literal["active", "inactive", "pending"]) -> None:
print(f"Status: {status}")
泛型(Generics)
TypeVar
from typing import TypeVar, Generic
T = TypeVar('T')
def first_element(lst: list[T]) -> T | None:
"""返回列表的第一个元素"""
return lst[0] if lst else None
# 使用
numbers = [1, 2, 3]
result: int = first_element(numbers)
strings = ["a", "b", "c"]
result: str = first_element(strings)
Generic 类
from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self):
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def peek(self) -> T:
return self._items[-1]
# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
print(int_stack.pop()) # 2
str_stack: Stack[str] = Stack()
str_stack.push("hello")
print(str_stack.pop()) # hello
泛型函数
from typing import TypeVar, Generic
T = TypeVar('T')
def reverse_list(lst: list[T]) -> list[T]:
"""反转列表"""
return lst[::-1]
def find_max(lst: list[T]) -> T | None:
"""找到最大值(需要T可比较)"""
if not lst:
return None
return max(lst)
Protocol(结构化子类型)
runtime_checkable
from typing import Protocol, runtime_checkable
@runtime_checkable
class Comparable(Protocol):
def __lt__(self, other: "Comparable") -> bool:
...
class Number:
def __init__(self, value: int):
self.value = value
def __lt__(self, other: "Number") -> bool:
return self.value < other.value
n1 = Number(1)
n2 = Number(2)
print(isinstance(n1, Comparable)) # True
Python 3.12+ 新特性(PEP 695)
Python 3.12 引入了更简洁的泛型语法,无需导入 TypeVar 和 Generic。
泛型函数(新语法)
# Python 3.12+ 新语法 - 无需导入 TypeVar
def first[T](elements: list[T]) -> T | None:
"""返回列表的第一个元素"""
return elements[0] if elements else None
# 使用
numbers = [1, 2, 3]
result: int = first(numbers)
strings = ["a", "b", "c"]
result: str = first(strings)
与旧语法对比:
# 旧语法(仍然可用)
from typing import TypeVar
T = TypeVar('T')
def first_old(elements: list[T]) -> T | None:
return elements[0] if elements else None
泛型类(新语法)
# Python 3.12+ 新语法
class Stack[T]:
def __init__(self):
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)
print(int_stack.pop()) # 2
str_stack: Stack[str] = Stack()
str_stack.push("hello")
print(str_stack.pop()) # hello
与旧语法对比:
# 旧语法
from typing import Generic, TypeVar
T = TypeVar('T')
class StackOld(Generic[T]):
def __init__(self):
self._items: list[T] = []
# ...
类型约束和边界
# 约束类型参数只能是特定类型
def process[T: (int, float)](value: T) -> T:
return value
# 使用边界限制类型
class Container[T: str]:
def __init__(self, value: T):
self.value = value
# 泛型类实例
container: Container[str] = Container("hello")
type 语句(类型别名)
Python 3.12 引入了 type 语句来声明类型别名:
# 简单类型别名
type Point = tuple[float, float]
type UserId = int
type Url = str
# 使用
def get_user(user_id: UserId) -> dict[str, str]:
return {"id": str(user_id), "name": "张三"}
# 泛型类型别名
type Vector[T] = list[tuple[T, T]]
type Result[T] = list[T] | tuple[T, str]
# 使用泛型类型别名
coordinates: Vector[float] = [(1.0, 2.0), (3.0, 4.0)]
与旧语法对比:
# 旧语法
from typing import TypeAlias
Point: TypeAlias = tuple[float, float]
Vector_old: TypeAlias = list[tuple[T, T]] # 需要 TypeVar
多类型参数
# 多个类型参数
def pair_to_tuple[T, U](a: T, b: U) -> tuple[T, U]:
return (a, b)
# 使用
result = pair_to_tuple("hello", 42) # tuple[str, int]
# 泛型类多参数
class Map[K, V]:
def __init__(self):
self._data: dict[K, V] = {}
def put(self, key: K, value: V) -> None:
self._data[key] = value
def get(self, key: K) -> V | None:
return self._data.get(key)
# 使用
str_int_map: Map[str, int] = Map()
str_int_map.put("one", 1)
print(str_int_map.get("one")) # 1
PEP 698:@override 装饰器
Python 3.12 引入了 @override 装饰器,用于标记子类中覆盖父类的方法:
from typing import override
class Animal:
def speak(self) -> str:
raise NotImplementedError
class Dog(Animal):
@override
def speak(self) -> str:
return "Woof!"
class Cat(Animal):
@override
def speak(self) -> str:
return "Meow!"
# 使用
dog = Dog()
print(dog.speak()) # Woof!
# 如果方法签名不匹配,类型检查器会报错
class InvalidOverride(Animal):
@override
def speak(self, sound: str) -> str: # 类型检查器会报错
return sound
@override 的作用:
- 明确标记方法是覆盖父类的方法
- 类型检查器会验证该方法确实存在于父类中
- 提高代码可读性和维护性
- 减少因方法签名不匹配导致的 bug
PEP 692:TypedDict 注解 **kwargs
使用 TypedDict 更精确地注解 **kwargs:
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
class Config(TypedDict, total=False):
host: str
port: int
def foo(movie: Unpack[Movie]) -> None:
print(movie)
# 使用
foo(name="Inception", year=2010)
def bar(config: Unpack[Config]) -> None:
print(config)
bar(host="localhost")
bar(host="localhost", port=8080)
这种方式可以精确地为每个 keyword 参数指定类型,而不是像之前那样只能使用统一的类型。
TypedDict
from typing import TypedDict, NotRequired
class Person(TypedDict):
name: str
age: int
class Config(TypedDict):
host: str
port: int
debug: bool
secret_key: NotRequired[str] # 可选字段
# 使用
person: Person = {"name": "张三", "age": 20}
config: Config = {"host": "localhost", "port": 8080, "debug": True}
特殊类型
NoReturn
from typing import NoReturn
def error_exit(message: str) -> NoReturn:
print(f"Error: {message}")
exit(1)
def process(data: str) -> None:
if not data:
error_exit("Data is empty")
# 后面不会执行
Never(Python 3.11+)
# Never 表示永远不会返回(类似 NoReturn)
def infinite_loop() -> Never:
while True:
pass
Type 类型
from typing import Type
def create_instance(cls: Type[object]) -> object:
return cls()
class MyClass:
pass
obj = create_instance(MyClass)
类型 narrowing
TypeGuard
from typing import TypeGuard
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
"""缩小类型范围"""
return all(isinstance(x, str) for x in val)
items: list[object] = ["a", "b", "c"]
if is_string_list(items):
# 这里 items 被缩小为 list[str]
print(items[0].upper()) # OK
TypeIs(Python 3.13+)
def is_positive(n: int) -> TypeIs[int]:
"""返回正数的类型"""
return n > 0
n: int = 5
if is_positive(n):
# n 在这里被缩小为正数
print(n + 1) # OK
类型注解最佳实践
1. 逐步添加类型
# 先不加类型
def process(data):
return data.get("value", 0)
# 逐步添加
def process(data: dict[str, int]) -> int:
return data.get("value", 0)
2. 使用类型别名
from typing import Callable
# 清晰的类型别名
UserId = int
UserName = str
UserCallback = Callable[[UserId, UserName], None]
def register(callback: UserCallback) -> None:
callback(1, "张三")
3. 避免过度类型注解
# 不需要注解的情况
name = "张三" # mypy 可以推断
count = len(items) # 明显
# 需要注解的情况
from typing import Optional
result = data.get("key") # 需要注解 Optional[str]
4. 使用 mypy 检查
pip install mypy
mypy your_file.py
typing 模块常用类型
| 类型 | 说明 |
|---|---|
int, str, float, bool | 基础类型 |
list[T], dict[K, V] | 容器类型 |
Optional[T] | 可能为 None |
Union[A, B] | 多种类型之一 |
Callable[[args], return] | 可调用对象 |
Type[T] | 类型本身 |
Any | 任意类型 |
None | 空值 |
小结
本章我们学习了:
- 基本的类型注解(变量、函数参数、返回值)
- 高级类型(Union、Optional、Callable、Tuple、Literal)
- 泛型(TypeVar、Generic)
- Protocol 协议
- TypedDict 字典类型
- 类型 narrowing(TypeGuard、TypeIs)
- 类型检查最佳实践
练习
- 为一个学生信息管理系统添加类型注解
- 创建一个泛型缓存类
- 使用 Protocol 定义一个可序列化协议
- 使用 TypedDict 定义 API 响应类型