# Python 类型提示 typing

**学习目标**：掌握 Python 类型提示系统，学会使用 typing 模块和现代语法编写类型安全的代码，理解静态类型检查的价值。

---

## 1. 为什么需要类型提示

### 1.1 动态类型的困境

```python
# 没有类型提示：参数和返回值类型不明确
def process(data):
    result = []
    for item in data:
        result.append(item * 2)
    return result

# 调用者无法知道：
# - data 应该是什么类型？列表？字典？
# - item 支持乘法吗？
# - 返回什么类型？
```

### 1.2 类型提示的好处

- **可读性**：代码即文档，一目了然
- **IDE 支持**：智能补全、错误提示
- **静态检查**：运行前发现类型错误
- **重构安全**：修改接口时自动发现影响范围
- **团队协作**：减少沟通成本

---

## 2. 基本类型提示

### 2.1 变量与函数

```python
# 变量类型提示
name: str = "张三"
age: int = 25
height: float = 1.75
is_student: bool = True

# 函数类型提示
def greet(name: str) -> str:
    return f"Hello, {name}"

def add(a: int, b: int) -> int:
    return a + b

def is_even(n: int) -> bool:
    return n % 2 == 0

# 多种返回类型
def parse_int(s: str) -> int | None:  # Python 3.10+ 语法
    try:
        return int(s)
    except ValueError:
        return None
```

### 2.2 Python 3.10+ 新语法

```python
# 旧写法（Python 3.9 及以下）
from typing import Optional, Union, List, Dict, Tuple

def old_style(x: Optional[int] = None) -> Union[str, int]:
    pass

data: List[int] = [1, 2, 3]
mapping: Dict[str, int] = {"a": 1}
point: Tuple[int, int] = (10, 20)

# 新写法（Python 3.10+）
def new_style(x: int | None = None) -> str | int:
    pass

data: list[int] = [1, 2, 3]
mapping: dict[str, int] = {"a": 1}
point: tuple[int, int] = (10, 20)
```

---

## 3. 复合类型

### 3.1 容器类型

```python
from typing import List, Dict, Set, Tuple, FrozenSet

# 列表
names: list[str] = ["Alice", "Bob"]
numbers: list[int | float] = [1, 2, 3.14]

# 字典
config: dict[str, str] = {"host": "localhost", "port": "8080"}
user_ages: dict[str, int] = {"Alice": 25, "Bob": 30}

# 集合
tags: set[str] = {"python", "web", "api"}

# 元组
# 固定长度元组
point: tuple[int, int] = (10, 20)
# 固定结构元组
record: tuple[str, int, bool] = ("Alice", 25, True)
# 可变长度同类型元组
scores: tuple[int, ...] = (90, 85, 92, 88)
```

### 3.2 嵌套类型

```python
# 嵌套列表
matrix: list[list[int]] = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# 字典的值为列表
groups: dict[str, list[str]] = {
    "admins": ["alice", "bob"],
    "users": ["charlie", "dave"]
}

# 复杂嵌套
api_response: dict[str, list[dict[str, int | str]]] = {
    "users": [
        {"id": 1, "name": "Alice", "age": 25},
        {"id": 2, "name": "Bob", "age": 30}
    ]
}
```

---

## 4. typing 模块常用类型

### 4.1 Optional 和 Union

```python
from typing import Optional, Union

# Optional[X] 等价于 X | None
def find_user(user_id: int) -> Optional[dict]:
    """可能返回用户字典或 None"""
    users = {1: {"name": "Alice"}}
    return users.get(user_id)

# Union 多种类型
def process_value(value: Union[int, str, float]) -> str:
    """处理多种类型的值"""
    return str(value)

# Python 3.10+ 可以用 | 语法
def process_value_new(value: int | str | float) -> str:
    return str(value)
```

### 4.2 Any 和 NoReturn

```python
from typing import Any, NoReturn

# Any：任意类型（尽量少用）
def log(message: Any) -> None:
    print(f"[LOG] {message}")

# NoReturn：函数永不返回
def fatal_error(message: str) -> NoReturn:
    import sys
    print(f"Fatal: {message}")
    sys.exit(1)
```

### 4.3 Callable（可调用对象）

```python
from typing import Callable

# Callable[[参数类型...], 返回类型]
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

# 使用
result = apply(lambda x, y: x + y, 3, 5)  # 8

# 无参数的 Callable
def run_task(task: Callable[[], None]) -> None:
    task()

# 不关心参数的 Callable
from typing import Any
handler: Callable[..., Any] = print
```

### 4.4 TypeVar（类型变量）

```python
from typing import TypeVar

# 定义类型变量
T = TypeVar('T')

# 泛型函数：输入和输出类型一致
def first(items: list[T]) -> T:
    return items[0]

# 使用
names: list[str] = ["Alice", "Bob"]
first_name: str = first(names)  # 类型推断为 str

numbers: list[int] = [1, 2, 3]
first_num: int = first(numbers)  # 类型推断为 int

# 约束类型变量
Number = TypeVar('Number', int, float)

def add(a: Number, b: Number) -> Number:
    return a + b

# add(1, 2)       ✓ 返回 int
# add(1.5, 2.5)   ✓ 返回 float
# add(1, 2.5)     ✗ 类型不匹配
```

---

## 5. 泛型（Generics）

### 5.1 泛型类

```python
from typing import Generic, TypeVar

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:
        if not self._items:
            raise IndexError("栈为空")
        return self._items.pop()
    
    def peek(self) -> T | None:
        return self._items[-1] if self._items else None
    
    def is_empty(self) -> bool:
        return len(self._items) == 0
    
    def size(self) -> int:
        return len(self._items)

# 使用
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")
text: str = str_stack.pop()
```

### 5.2 泛型方法

```python
from typing import TypeVar, K, V

K = TypeVar('K')
V = TypeVar('V')

class BinaryTree(Generic[K, V]):
    """泛型二叉树节点"""
    
    def __init__(self, key: K, value: V) -> None:
        self.key = key
        self.value = value
        self.left: BinaryTree[K, V] | None = None
        self.right: BinaryTree[K, V] | None = None
    
    def insert(self, key: K, value: V) -> None:
        if key < self.key:
            if self.left is None:
                self.left = BinaryTree(key, value)
            else:
                self.left.insert(key, value)
        elif key > self.key:
            if self.right is None:
                self.right = BinaryTree(key, value)
            else:
                self.right.insert(key, value)
```

---

## 6. Protocol（结构化类型）

```python
from typing import Protocol

# 定义协议（类似接口）
class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Serializable(Protocol):
    def to_json(self) -> str:
        ...

# 实现协议（不需要显式继承）
class Circle:
    def draw(self) -> None:
        print("绘制圆形")

class Square:
    def draw(self) -> None:
        print("绘制方形")

# 使用协议作为类型
def render(shape: Drawable) -> None:
    shape.draw()

# Circle 和 Square 都实现了 draw 方法，自动满足 Drawable 协议
render(Circle())  # ✓
render(Square())  # ✓
```

---

## 7. TypedDict（类型化字典）

```python
from typing import TypedDict

# 定义类型化字典
class UserDict(TypedDict):
    id: int
    name: str
    email: str
    age: int  # 必需字段

# 可选字段（Python 3.11+）
class UserDictOptional(TypedDict, total=False):
    id: int
    name: str
    email: str  # 所有字段可选

# 混合必需和可选（Python 3.11+）
class Movie(TypedDict):
    title: str
    year: int

class MovieWithRating(Movie, total=False):
    rating: float

# 使用
user: UserDict = {
    "id": 1,
    "name": "张三",
    "email": "zhangsan@example.com",
    "age": 25
}
# 类型检查器会验证字段完整性
```

---

## 8. Literal 和 Final

```python
from typing import Literal, Final

# Literal：字面量类型
def set_mode(mode: Literal["dev", "prod", "test"]) -> None:
    print(f"设置模式: {mode}")

set_mode("dev")   # ✓
set_mode("debug") # ✗ 类型错误

# 常量定义
MAX_SIZE: Final[int] = 100
API_BASE_URL: Final[str] = "https://api.example.com"

# Final 防止重新赋值
class Config:
    VERSION: Final[str] = "1.0.0"
    
# VERSION = "2.0"  # 类型检查器会报错
```

---

## 9. 类型别名

```python
from typing import TypeAlias

# 简单别名
JSON: TypeAlias = dict[str, 'JSON'] | list['JSON'] | str | int | float | bool | None

# 使用
def parse_json(text: str) -> JSON:
    import json
    return json.loads(text)

# Python 3.12+ 新语法
# type JSON = dict[str, JSON] | list[JSON] | str | int | float | bool | None

# 复杂类型别名
UserId = int
UserName = str
UserMap = dict[UserId, UserName]

users: UserMap = {1: "Alice", 2: "Bob"}
```

---

## 10. 重载（Overload）

```python
from typing import overload

# 重载签名（仅用于类型检查）
@overload
def get_value(key: str) -> str: ...
@overload
def get_value(key: int) -> int: ...
@overload
def get_value(key: None) -> None: ...

# 实际实现
def get_value(key):
    """根据 key 类型返回不同类型"""
    if key is None:
        return None
    elif isinstance(key, str):
        return f"string: {key}"
    elif isinstance(key, int):
        return key * 2

# 类型检查器知道：
result1: str = get_value("hello")   # ✓
result2: int = get_value(42)        # ✓
result3: None = get_value(None)     # ✓
```

---

## 11. 静态类型检查

### 11.1 使用 mypy

```bash
# 安装
pip install mypy

# 检查单个文件
mypy script.py

# 检查整个项目
mypy src/

# 严格模式
mypy --strict src/

# 忽略缺失导入
mypy --ignore-missing-imports src/
```

### 11.2 配置文件

```toml
# pyproject.toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
```

### 11.3 类型错误示例

```python
# mypy 会检测到的错误
def add(a: int, b: int) -> int:
    return a + b

add(1, 2)        # ✓
add("1", "2")    # ✗ error: Argument 1 to "add" has incompatible type "str"
add(1, 2, 3)     # ✗ error: Too many arguments
result: str = add(1, 2)  # ✗ error: Incompatible types in assignment

# 忽略特定行
result = some_untyped_function()  # type: ignore
```

---

## 12. Pydantic 与类型提示

```python
# pip install pydantic
from pydantic import BaseModel, EmailStr, Field
from typing import Optional

class User(BaseModel):
    """使用类型提示进行数据验证"""
    id: int
    name: str = Field(..., min_length=2, max_length=50)
    email: EmailStr
    age: int = Field(..., ge=0, le=150)
    bio: Optional[str] = None
    
# 自动验证
user = User(
    id=1,
    name="张三",
    email="zhangsan@example.com",
    age=25
)

# 类型错误会抛出异常
try:
    invalid_user = User(
        id="not_an_int",  # 类型错误
        name="张",
        email="invalid-email",
        age=200
    )
except Exception as e:
    print(f"验证失败: {e}")
```

---

## 13. 小结

| 概念 | 语法 | 说明 |
|------|------|------|
| 基本类型 | `str`, `int`, `float`, `bool` | 内置类型直接使用 |
| Optional | `X \| None` | 可空类型 |
| 容器 | `list[T]`, `dict[K, V]` | 泛型容器 |
| Callable | `Callable[[A, B], R]` | 函数类型 |
| TypeVar | `TypeVar('T')` | 泛型类型变量 |
| Protocol | `class P(Protocol)` | 结构化接口 |
| Literal | `Literal["a", "b"]` | 字面量类型 |
| Final | `Final[int]` | 不可变常量 |
| mypy | `mypy src/` | 静态类型检查器 |

---

## 14. 练习题

1. 为之前写的所有函数添加类型提示，并用 mypy 检查。
2. 实现一个泛型缓存类 `Cache[K, V]`，支持 get/set/delete。
3. 使用 Protocol 定义一个 `Iterable` 接口，并实现它。
4. 使用 Pydantic 创建一个 API 请求模型，包含数据验证。

---

> **下节预告**：我们将学习代码风格规范和 Linting 工具，让代码更加整洁专业。

