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. 最佳实践

渐进式类型
  1. 从公共 API 开始:函数签名、类方法

  2. 逐步添加:不需要一次性完成所有类型注解

  3. 使用 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
注意事项
  1. 类型提示不影响运行时(除非使用 @runtime_checkable

  2. 避免过度使用 Any:失去类型检查意义

  3. 使用 from __future__ import annotations:延迟求值,支持前向引用

  4. 保持一致性:团队统一风格和严格程度