8Fluent Python(第2版)

类型提示

静态类型

类型注解Protocol泛型类型检查

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第8章:类型提示

一、导读

1.1 本章学习目标

第8章讲解 Python 的类型提示系统:类型注解、Protocol、泛型、类型检查。

本章核心学习目标:

  • 掌握类型注解:变量、函数、类的类型标注
  • 理解 Protocol:结构化子类型
  • 掌握泛型:Generic 和 TypeVar
  • 了解 mypy 类型检查

1.2 为什么本章如此重要

类型提示让 Python 代码更安全、更易维护。大型项目尤其需要类型检查来捕获错误。

1.3 核心问题引导

类型提示影响运行时性能吗?

Protocol 和 ABC 有什么区别?

泛型如何在 Python 中使用?


二、核心概念详解

2.1 类型注解

python
from typing import Optional, Union

def greet(name: str, greeting: str = "Hello") -> str:
    return f"{greeting}, {name}!"

def process(data: list[int]) -> dict[str, int]:
    return {"sum": sum(data), "count": len(data)}

# Optional = Union[X, None]
def find_user(user_id: int) -> Optional[dict]:
    # 可能返回 dict 或 None
    pass

# 复杂类型
from typing import Callable, Iterator

def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

def count_to(n: int) -> Iterator[int]:
    for i in range(n):
        yield i

2.2 Protocol

python
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Circle:
    def __init__(self, radius: float):
        self.radius = radius
    
    def draw(self) -> None:
        print(f"Drawing circle with radius {self.radius}")

class Square:
    def __init__(self, side: float):
        self.side = side
    
    def draw(self) -> None:
        print(f"Drawing square with side {self.side}")

# 不需要继承 Drawable,只要有 draw 方法就行
def render(shape: Drawable) -> None:
    shape.draw()

render(Circle(5))   # OK
render(Square(3))   # OK (结构化子类型)

2.3 泛型

python
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]
    
    def is_empty(self) -> bool:
        return len(self._items) == 0

int_stack: Stack[int] = Stack()
int_stack.push(42)
# int_stack.push("hello")  # mypy 报错

str_stack: Stack[str] = Stack()
str_stack.push("hello")

三、实战案例

3.1 案例一:类型安全的 Repository

python
from typing import Protocol, TypeVar, Generic
from dataclasses import dataclass

T = TypeVar('T')
ID = TypeVar('ID')

@dataclass
class User:
    id: int
    name: str
    email: str

class Repository(Protocol[T, ID]):
    def get(self, id: ID) -> T | None: ...
    def save(self, entity: T) -> T: ...
    def delete(self, id: ID) -> bool: ...
    def find_all(self) -> list[T]: ...

class UserRepository:
    def __init__(self):
        self._users: dict[int, User] = {}
    
    def get(self, id: int) -> User | None:
        return self._users.get(id)
    
    def save(self, user: User) -> User:
        self._users[user.id] = user
        return user
    
    def delete(self, id: int) -> bool:
        return self._users.pop(id, None) is not None
    
    def find_all(self) -> list[User]:
        return list(self._users.values())

四、本章小结

类型提示提高代码可读性和安全性

Protocol 提供结构化子类型,无需继承

泛型让类和函数支持多种类型


五、思考与练习

类型提示在运行时会被检查吗?

如何用 TypeVar 约束类型?


8/8类型提示
0%