1.7. 类型提示
Python 3.5+ 引入了类型提示(Type Hints),它能提高代码可读性,帮助 IDE 提供更好的支持,并能配合类型检查工具发现潜在错误。
1.7.1. 基础类型提示
1.7.1.1. 基本类型
# 变量注解
name: str = "Alice"
age: int = 30
height: float = 1.75
is_active: bool = True
# 函数注解
def greet(name: str) -> str:
return f"Hello, {name}!"
# 可选参数
def greet_with_default(name: str = "World") -> str:
return f"Hello, {name}!"
1.7.1.2. 容器类型
from typing import List, Dict, Set, Tuple
# Python 3.9+ 可以直接使用内置类型
names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 90, "Bob": 85}
unique_ids: set[int] = {1, 2, 3}
point: tuple[int, int] = (10, 20)
# Python 3.8 及以下需要从 typing 导入
from typing import List, Dict, Set, Tuple
names: List[str] = ["Alice", "Bob"]
scores: Dict[str, int] = {"Alice": 90}
# 可变长度元组
numbers: tuple[int, ...] = (1, 2, 3, 4, 5)
# 嵌套类型
matrix: list[list[int]] = [[1, 2], [3, 4]]
users: dict[str, dict[str, int]] = {
"alice": {"age": 30, "score": 90}
}
1.7.2. Optional 和 Union
from typing import Optional, Union
# Optional[X] 等价于 Union[X, None]
def find_user(user_id: int) -> Optional[str]:
"""可能返回 None"""
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
# Union: 多个类型之一
def process(value: Union[int, str]) -> str:
return str(value)
# Python 3.10+ 可以使用 | 语法
def process_new(value: int | str) -> str:
return str(value)
def find_user_new(user_id: int) -> str | None:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
1.7.3. Callable 和函数类型
from typing import Callable
# 函数类型:Callable[[参数类型...], 返回类型]
def apply(func: Callable[[int], int], value: int) -> int:
return func(value)
def double(x: int) -> int:
return x * 2
result = apply(double, 5) # 10
# 可变参数的 Callable
from typing import Callable, Any
def log_call(func: Callable[..., Any]) -> Callable[..., Any]:
def wrapper(*args: Any, **kwargs: Any) -> Any:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
1.7.4. 泛型
1.7.4.1. TypeVar
from typing import TypeVar, List, Sequence
T = TypeVar('T')
def first(items: Sequence[T]) -> T:
"""返回序列的第一个元素,保持类型信息"""
return items[0]
# 类型检查器知道返回类型
strings = ["a", "b", "c"]
first_str: str = first(strings) # str
numbers = [1, 2, 3]
first_num: int = first(numbers) # int
# 受限的 TypeVar
Number = TypeVar('Number', int, float)
def add(a: Number, b: Number) -> Number:
return a + b
# 绑定的 TypeVar
from typing import TypeVar
Comparable = TypeVar('Comparable', bound='Comparable')
class Comparable:
def __lt__(self, other: 'Comparable') -> bool:
...
1.7.4.2. Generic 类
from typing import TypeVar, Generic, Optional
T = 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()
def peek(self) -> Optional[T]:
return self._items[-1] if self._items else None
def is_empty(self) -> bool:
return len(self._items) == 0
# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
value: int = int_stack.pop()
str_stack: Stack[str] = Stack()
str_stack.push("hello")
1.7.5. Protocol(结构化子类型)
from typing import Protocol, runtime_checkable
class Drawable(Protocol):
"""定义接口:任何有 draw 方法的类型"""
def draw(self) -> str:
...
class Circle:
def draw(self) -> str:
return "Drawing circle"
class Square:
def draw(self) -> str:
return "Drawing square"
def render(shape: Drawable) -> None:
print(shape.draw())
# Circle 和 Square 没有显式继承 Drawable
# 但它们符合 Drawable 协议(鸭子类型)
render(Circle()) # OK
render(Square()) # OK
# 运行时检查
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self) -> None:
pass
print(isinstance(File(), Closeable)) # True
1.7.6. TypedDict
from typing import TypedDict, Required, NotRequired
class UserDict(TypedDict):
name: str
age: int
email: str
# 所有键都是必需的
user: UserDict = {
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
# Python 3.11+: 控制必需/可选
class UserDictNew(TypedDict):
name: Required[str]
age: Required[int]
email: NotRequired[str] # 可选
# total=False 使所有键可选
class PartialUser(TypedDict, total=False):
name: str
age: int
email: str
1.7.7. Literal 和 Final
from typing import Literal, Final
# Literal: 限制具体值
def set_mode(mode: Literal["read", "write", "append"]) -> None:
print(f"Mode: {mode}")
set_mode("read") # OK
# set_mode("delete") # 类型检查器报错
# Final: 不可重新赋值
MAX_CONNECTIONS: Final = 100
# MAX_CONNECTIONS = 200 # 类型检查器报错
# Final 类属性
class Config:
DEBUG: Final[bool] = False
# 子类不能覆盖 Final 属性
1.7.8. 实用类型
1.7.8.1. Any、Never、NoReturn
from typing import Any, NoReturn, Never
# Any: 任意类型(跳过类型检查)
def process_anything(value: Any) -> Any:
return value
# NoReturn: 函数永不返回
def raise_error() -> NoReturn:
raise RuntimeError("Error!")
def infinite_loop() -> NoReturn:
while True:
pass
# Never (Python 3.11+): 不可能的类型
def assert_never(value: Never) -> NoReturn:
raise AssertionError(f"Unexpected value: {value}")
1.7.8.2. Self(Python 3.11+)
from typing import Self
class Builder:
def __init__(self) -> None:
self._value = 0
def add(self, n: int) -> Self:
self._value += n
return self
def multiply(self, n: int) -> Self:
self._value *= n
return self
# 链式调用
result = Builder().add(5).multiply(2)._value
1.7.8.3. Annotated
from typing import Annotated
# 附加元数据到类型
UserId = Annotated[int, "user ID"]
PositiveInt = Annotated[int, "must be positive"]
def get_user(user_id: UserId) -> str:
return f"User {user_id}"
# 常与验证库配合使用(如 Pydantic)
from pydantic import BaseModel, Field
class User(BaseModel):
name: Annotated[str, Field(min_length=1, max_length=50)]
age: Annotated[int, Field(ge=0, le=150)]
1.7.9. 类型检查工具
1.7.9.1. mypy
# 安装
pip install mypy
# 检查单个文件
mypy script.py
# 检查整个项目
mypy src/
# 严格模式
mypy --strict src/
1.7.9.2. 配置文件(mypy.ini 或 pyproject.toml)
# mypy.ini
[mypy]
python_version = 3.10
warn_return_any = True
warn_unused_ignores = True
disallow_untyped_defs = True
# 忽略特定模块
[mypy.plugins.external_lib.*]
ignore_missing_imports = True
1.7.9.3. 内联忽略
# 忽略特定行
x = some_untyped_function() # type: ignore
# 忽略特定错误
x = some_function() # type: ignore[arg-type]
# 揭示推断类型(调试用)
reveal_type(x) # mypy 会输出推断的类型
1.7.10. 最佳实践
渐进式类型
从公共 API 开始:函数签名、类方法
逐步添加:不需要一次性完成所有类型注解
使用
py.typed标记:表明包支持类型检查
常见模式
# 返回 self 用于链式调用
from __future__ import annotations
class Builder:
def method(self) -> Builder: # 或使用 Self
return self
# 类型别名提高可读性
from typing import TypeAlias
UserId: TypeAlias = int
UserDict: TypeAlias = dict[str, Any]
# 前向引用(类引用自身)
class Node:
def __init__(self, children: list['Node']) -> None:
self.children = children
注意事项
类型提示不影响运行时(除非使用
@runtime_checkable)避免过度使用
Any:失去类型检查意义使用
from __future__ import annotations:延迟求值,支持前向引用保持一致性:团队统一风格和严格程度