02-类型进阶应用
Python 版本要求:3.11+ 本章使用 Python 3.11+ 现代语法,主要改进包括:
- 使用内置泛型(
list[int]而非List[int])- 使用联合操作符(
int | str而非Union[int, str])- 使用
Self类型而非字符串注解- 无需从 typing 导入基础容器类型
深入掌握 Python 类型系统的进阶特性。
概念铺垫
Python 类型系统的进阶能力包括:泛型编程(TypeVar/Generic)、结构化子类型(Protocol)、精确字典类型(TypedDict)、运行时类型检查(get_type_hints)和类型收窄(TypeGuard)。这些特性帮助你在不丢失类型安全的前提下编写通用、可复用的代码。
L1 理解层:会用
1. 泛型深入理解
1.1 问题引入
场景: 你想写一个通用的栈类,能存储任意类型的元素。
# 每种类型都要写一个类?太麻烦了!
class IntStack:
def push(self, item: int) -> None: ...
def pop(self) -> int: ...
class StrStack:
def push(self, item: str) -> None: ...
def pop(self) -> str: ...
# 能不能用一个类处理所有类型?问题: 如何写一个类,既能处理整数,也能处理字符串?
1.2 概念解释
泛型(Generic): 定义时不指定具体类型,使用时再确定。
┌─────────────────────────────────────────────────────┐
│ 泛型语法结构 │
├─────────────────────────────────────────────────────┤
│ │
│ 泛型函数: │
│ ─────────────────────────────────────── │
│ def func(items: list[T]) -> T: │
│ ↑ ↑ ↑ │
│ 函数名 参数类型 返回类型 │
│ │
│ 泛型类: │
│ ─────────────────────────────────────── │
│ class Box(Generic[T]): │
│ ↑ ↑ │
│ 泛型基类 类型参数 │
│ │
│ 类型变量定义: │
│ ─────────────────────────────────────── │
│ T = TypeVar('T') → 无约束泛型 │
│ N = TypeVar('N', int, float) → 约束泛型 │
│ │
│ 使用示例 → 解释: │
│ Stack[int] → T 替换为 int,整数栈 │
│ Stack[str] → T 替换为 str,字符串栈 │
│ │
└─────────────────────────────────────────────────────┘TypeVar 关键理解点:
┌─────────────────────────────────────────────────────────────┐
│ TypeVar 使用要点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. TypeVar 创建类型变量(占位符) │
│ ───────────────────────────────────────────── │
│ • 类似数学中的 x、y 未知数,用具体值替换后才确定 │
│ • T 本身不是类型,T 被替换为 int/str 后才是具体类型 │
│ • 替换发生在静态分析时,不是运行时 │
│ │
│ 2. 'T' 和 T 的含义 │
│ ───────────────────────────────────────────── │
│ T = TypeVar('T') │
│ ↑ ↑ │
│ 变量名 字符串名称 │
│ │
│ • 'T' 是字符串:类型检查器的名称标识 │
│ • T 是变量:代码中引用这个类型变量 │
│ • 两者保持一致便于理解和调试 │
│ │
│ 3. 类型变量替换时机 │
│ ───────────────────────────────────────────── │
│ 定义时:def first(items: list[T]) → T # T 未替换 │
│ 使用时:first([1, 2, 3]) # T → int │
│ 推断者:类型检查器(静态分析),不是运行时 │
│ │
│ 4. 常见混淆澄清 │
│ ───────────────────────────────────────────── │
│ ❌ T 是特殊类型 → ✅ T 是占位符 │
│ ❌ TypeVar 创建类型 → ✅ TypeVar 声明类型变量名 │
│ ❌ 运行时 T 变具体 → ✅ 替换在静态分析时完成 │
│ │
│ 5. 与函数参数类比 │
│ ───────────────────────────────────────────── │
│ 函数:def f(x): ... x 是值变量 │
│ 类型:def f(items: list[T]): ... T 是类型变量 │
│ │
│ • TypeVar('T') → 声明"类型位置暂用 T 表示" │
│ • list[T] → "某个类型的列表,类型待定" │
│ • list[int] → T 替换为 int,现在是"整数列表" │
│ │
└─────────────────────────────────────────────────────────────┘1.3 最简示例
from typing import TypeVar
T = TypeVar('T')
def first(items: list[T]) -> T:
return items[0]
numbers = [1, 2, 3]
first_number = first(numbers) # 类型是 int
strings = ["a", "b", "c"]
first_string = first(strings) # 类型是 str关键代码解释
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
TypeVar('T') | 定义类型变量 T | T 是占位符,使用时替换为具体类型 |
list[T] | 参数是 T 类型元素的列表 | 不限制具体类型,任意类型都可以 |
-> T | 返回值类型为 T | 保证返回类型与元素类型一致(类型保持) |
first(numbers) | T 替换为 int | 根据参数类型自动推断 |
first(strings) | T 替换为 str | 根据参数类型自动推断 |
1.4 详细说明
泛型函数:
from typing import TypeVar
T = TypeVar('T')
def reverse(items: list[T]) -> list[T]:
"""反转列表,保持类型"""
return items[::-1]
def get_middle(items: list[T]) -> T:
"""获取中间元素"""
return items[len(items) // 2]
# 使用
nums = reverse([1, 2, 3]) # list[int]
chars = reverse(["a", "b"]) # list[str]
mid_num = get_middle([1, 2, 3]) # int: 2
mid_char = get_middle(["a", "b", "c"]) # str: "b"泛型类:
泛型函数用 TypeVar 声明类型变量,泛型类还需要 Generic 基类。
┌─────────────────────────────────────────────────────────────┐
│ 泛型类关键概念 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Generic 是什么? │
│ ───────────────────────────────────────────── │
│ • Generic 是 typing 模块的基类 │
│ • 用于标记"这个类是泛型类" │
│ • Generic[T] 表示"这个类有一个类型参数 T" │
│ │
│ 2. 为什么要继承 Generic[T]? │
│ ───────────────────────────────────────────── │
│ • 不继承:类型检查器不知道 Box 是泛型 │
│ • 继承后:Box[int]、Box[str] 才是有效的泛型类型 │
│ • Generic[T] 是"声明"机制,不是"继承"行为 │
│ │
│ 3. Generic 和 TypeVar 的关系 │
│ ───────────────────────────────────────────── │
│ T = TypeVar('T') # 先声明类型变量 T │
│ class Box(Generic[T]): # 再用 T 作为类的类型参数 │
│ │
│ • TypeVar:创建类型变量(占位符) │
│ • Generic:把类型变量绑定到类 │
│ • 两者配合:类才能支持泛型 │
│ │
│ 4. 泛型类的工作流程 │
│ ───────────────────────────────────────────── │
│ 定义:class Box(Generic[T]): # T 是占位符 │
│ 使用:Box[int] # T 替换为 int │
│ 实例:box = Box(42) # 创建存储 int 的盒子 │
│ │
│ 5. 对比:普通类 vs 泛型类 │
│ ───────────────────────────────────────────── │
│ 普通类: │
│ class IntBox: │
│ def __init__(self, item: int): ... │
│ def get(self) -> int: ... │
│ # 只能存储 int,需要写 StrBox、FloatBox... │
│ │
│ 泛型类: │
│ class Box(Generic[T]): │
│ def __init__(self, item: T): ... │
│ def get(self) -> T: ... │
│ # 一个类存储所有类型:Box[int]、Box[str]、Box[User]... │
│ │
└─────────────────────────────────────────────────────────────┘代码示例:
from typing import TypeVar, Generic
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, item: T) -> None:
self._item = item
def get(self) -> T:
return self._item
def set(self, item: T) -> None:
self._item = item
int_box: Box[int] = Box(42)
value: int = int_box.get()
str_box: Box[str] = Box("hello")
text: str = str_box.get()关键代码解释:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
Generic[T] | 继承泛型基类,声明 T 为类型参数 | 让类支持泛型,一个类处理多种类型 |
item: T | 参数类型为 T | 存入的类型与声明类型一致 |
-> T | 返回类型为 T | 取出的类型与存入类型一致 |
Box[int] | 使用时指定 T 为 int | 创建整数盒子,类型检查器知道内部是 int |
Box[str] | 使用时指定 T 为 str | 创建字符串盒子,类型检查器知道内部是 str |
1.5 渐进复杂
Python 3.12+ 新语法:泛型类参数语法
# Python 3.12+:直接在类定义中使用类型参数(无需 TypeVar)
class Stack[T]:
"""泛型栈(Python 3.12+ 新语法)"""
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()
# Python 3.11 兼容写法(使用 TypeVar)
from typing import TypeVar, Generic
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()泛型约束:
from typing import TypeVar
Number = TypeVar('Number', int, float)
def double(value: Number) -> Number:
return value * 2
result1 = double(5) # int: 10
result2 = double(5.0) # float: 10.0
# double("hello") # 类型检查器警告!关键代码解释:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
TypeVar('Number', int, float) | 定义约束类型变量 | Number 只能是 int 或 float,不能是其他类型 |
value: Number | 参数类型为 Number | 接收 int 或 float,但拒绝 str 等 |
-> Number | 返回类型为 Number | 返回类型与参数类型一致 |
double("hello") | 类型检查器警告 | 字符串不满足 Number 的约束条件 |
多类型变量:
from typing import TypeVar, Generic
K = TypeVar('K')
V = TypeVar('V')
class Pair(Generic[K, V]):
def __init__(self, key: K, value: V) -> None:
self.key = key
self.value = value
def get_key(self) -> K:
return self.key
def get_value(self) -> V:
return self.value
pair: Pair[str, int] = Pair("age", 25)
key: str = pair.get_key()
value: int = pair.get_value()关键代码解释:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
K = TypeVar('K') | 定义类型变量 K | 用于键的类型 |
V = TypeVar('V') | 定义类型变量 V | 用于值的类型 |
Generic[K, V] | 多类型参数泛型 | K 和 V 可以是不同类型 |
Pair[str, int] | K=str, V=int | 键是字符串,值是整数 |
key: str | 自动推断为 str | 与声明时 K=str 一致 |
value: int | 自动推断为 int | 与声明时 V=int 一致 |
1.6 实际应用
泛型栈:
from typing import TypeVar, Generic
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:
"""查看栈顶"""
if not self._items:
raise IndexError("栈为空")
return self._items[-1]
def is_empty(self) -> bool:
return len(self._items) == 0
# 整数栈
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("a")
str_stack.push("b")
print(str_stack.pop()) # "b"泛型仓储:
from typing import TypeVar, Generic
from dataclasses import dataclass
T = TypeVar('T')
@dataclass
class Entity:
id: int
class Repository(Generic[T]):
"""泛型仓储"""
def __init__(self) -> None:
self._storage: list[T] = []
def add(self, item: T) -> int:
"""添加并返回索引"""
self._storage.append(item)
return len(self._storage) - 1
def get(self, index: int) -> T | None:
"""按索引获取"""
if 0 <= index < len(self._storage):
return self._storage[index]
return None
def get_all(self) -> list[T]:
"""获取所有"""
return self._storage.copy()
# 使用
class User(Entity):
def __init__(self, id: int, name: str) -> None:
super().__init__(id)
self.name = name
user_repo: Repository[User] = Repository()
user_repo.add(User(1, "张三"))
user_repo.add(User(2, "李四"))
user = user_repo.get(0)
if user:
print(f"用户:{user.name}")2. 协议 Protocol
2.1 问题引入
场景: 你想定义"有 draw 方法的对象",但不关心具体类型。
# 如何定义"有 draw 方法的类型"?
def render(shape):
shape.draw() # 希望任何有 draw 方法的对象都能用
# 传统继承方式:需要显式继承
class ShapeBase:
def draw(self) -> None: ...
class Circle(ShapeBase):
def draw(self) -> None:
print("画圆")
# 问题:已有的类没有继承 ShapeBase,能用吗?
class ExistingCircle:
def draw(self) -> None:
print("画圆")
# render(ExistingCircle()) # 类型检查器可能警告问题: 如何定义"有某种方法"的类型,而不要求显式继承?
2.2 概念解释
协议(Protocol): 基于方法的类型,只要有这些方法就符合。
┌─────────────────────────────────────────────────────┐
│ 协议语法结构 │
├─────────────────────────────────────────────────────┤
│ │
│ 定义协议: │
│ ─────────────────────────────────────── │
│ class Drawable(Protocol): │
│ ↑ │
│ 必须继承 Protocol │
│ def draw(self) -> None: ... │
│ ↑ │
│ 方法签名(...表示空实现) │
│ │
│ 实现类: │
│ ─────────────────────────────────────── │
│ class Circle: # 无需继承 Drawable │
│ def draw(self) -> None: # 只要有方法即可 │
│ ... │
│ │
│ 使用示例 → 解释: │
│ render(Circle()) → Circle 有 draw,符合协议 │
│ render(123) → int 无 draw,类型检查器警告 │
│ │
│ 核心特点: │
│ • 结构化类型(鸭子类型) │
│ • 无需显式继承 │
│ • 只要有方法就符合 │
│ │
└─────────────────────────────────────────────────────┘2.3 最简示例
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Circle:
def draw(self) -> None:
print("画圆")
class Square:
def draw(self) -> None:
print("画正方形")
def render(shape: Drawable) -> None:
shape.draw()
render(Circle())
render(Square())关键代码解释
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
class Drawable(Protocol) | 定义协议类 | Protocol 表示这是一个协议,不是普通类 |
def draw(self) -> None: ... | 声明方法签名 | ... 表示方法体为空,只声明不实现 |
class Circle | 普通类 | 无需继承 Drawable,自动符合协议 |
def draw(self) -> None | 实现方法 | 只要有 draw 方法,就符合 Drawable 协议 |
shape: Drawable | 参数类型为协议 | 任何有 draw 方法的对象都可以传入 |
render(Circle()) | Circle 符合 Drawable | 因为 Circle 有 draw 方法 |
2.4 详细说明
定义协议:
from typing import Protocol
# 协议定义:列出方法签名
class Comparable(Protocol):
def compare_to(self, other: "Comparable") -> int:
"""返回:负数、零、正数"""
...
class Sized(Protocol):
def __len__(self) -> int: ...
class Iterable(Protocol):
def __iter__(self) -> "Iterable": ...协议继承:
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Fillable(Protocol):
def fill(self) -> None: ...
# 组合协议
class Shape(Drawable, Fillable, Protocol):
def move(self, x: int, y: int) -> None: ...
# 实现类:需要所有方法
class Circle:
def draw(self) -> None:
print("画圆")
def fill(self) -> None:
print("填充圆")
def move(self, x: int, y: int) -> None:
print(f"移动到 ({x}, {y})")
def render_shape(shape: Shape) -> None:
shape.draw()
shape.fill()
shape.move(0, 0)
render_shape(Circle())2.5 渐进复杂
运行时检查:
from typing import Protocol, runtime_checkable
@runtime_checkable
class Serializable(Protocol):
def to_json(self) -> str: ...
class User:
def to_json(self) -> str:
return '{"name": "张三", "age": 25}'
class PlainClass:
pass
# 可以用 isinstance 检查
print(isinstance(User(), Serializable)) # True
print(isinstance(PlainClass(), Serializable)) # False协议属性:
from typing import Protocol
class Named(Protocol):
name: str # 属性协议
class Person:
def __init__(self, name: str) -> None:
self.name = name
def greet(obj: Named) -> str:
return f"你好,{obj.name}"
greet(Person("张三")) # "你好,张三"2.6 实际应用
可比较协议:
from typing import Protocol
class Comparable(Protocol):
def compare_to(self, other: "Comparable") -> int: ...
class Person:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
def compare_to(self, other: "Person") -> int:
return self.age - other.age
def __repr__(self) -> str:
return f"Person({self.name}, {self.age})"
def find_max(items: list[Comparable]) -> Comparable:
"""找到最大的元素"""
if not items:
raise ValueError("列表为空")
max_item = items[0]
for item in items[1:]:
if item.compare_to(max_item) > 0:
max_item = item
return max_item
# 使用
people = [
Person("张三", 25),
Person("李四", 18),
Person("王五", 30)
]
max_person = find_max(people)
print(max_person) # Person(王五, 30)3. TypedDict 字典类型
3.1 问题引入
场景: 字典的每个字段有不同类型,如何标注?
# 普通字典注解:只知道是 dict
user: dict[str, str | int] = {
"name": "张三",
"age": 25,
"email": "test@example.com"
}
# 问题:不知道哪些字段是必需的
# 问题:不知道每个字段的具体类型
name = user["name"] # 类型是 str | int,不够精确问题: 如何精确标注字典的每个字段类型?
3.2 概念解释
TypedDict: 定义字典的键名和每个键的类型。
┌─────────────────────────────────────────────────────┐
│ TypedDict 语法结构 │
├─────────────────────────────────────────────────────┤
│ │
│ 定义 TypedDict: │
│ ─────────────────────────────────────── │
│ class UserDict(TypedDict): │
│ ↑ │
│ 必须继承 TypedDict │
│ id: int # 字段名 + 类型 │
│ name: str # 字段名 + 类型 │
│ email: str # 字段名 + 类型 │
│ │
│ 可选字段: │
│ ─────────────────────────────────────── │
│ class UserDict(TypedDict, total=False): │
│ ↑ │
│ 所有字段变为可选 │
│ │
│ 使用示例 → 解释: │
│ user["id"] → IDE 知道返回 int │
│ user["name"] → IDE 知道返回 str │
│ user["age"] → 类型检查器警告:未定义的字段 │
│ │
│ 对比普通 dict[str, Any]: │
│ • 精确标注每个字段类型 │
│ • IDE 提示字段名 │
│ • 类型检查器验证字段 │
│ │
└─────────────────────────────────────────────────────┘3.3 最简示例
from typing import TypedDict
class UserDict(TypedDict):
id: int
name: str
email: str
user: UserDict = {
"id": 1,
"name": "张三",
"email": "test@example.com"
}
print(user["id"])
print(user["name"])关键代码解释
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
class UserDict(TypedDict) | 定义字典类型类 | TypedDict 不是普通类,是类型声明工具 |
id: int | 字段名和类型 | 声明 id 字段必须是 int 类型 |
name: str | 字段名和类型 | 声明 name 字段必须是 str 类型 |
email: str | 字段名和类型 | 声明 email 字段必须是 str 类型 |
user: UserDict | 变量类型为 UserDict | 类型检查器会验证字典内容是否符合定义 |
user["id"] | 访问字段 | IDE 知道返回值是 int 类型(精确类型) |
3.4 详细说明
必需字段:
from typing import TypedDict
class UserDict(TypedDict):
"""所有字段都是必需的"""
id: int
name: str
email: str
# 缺少字段会报错
# user: UserDict = {"id": 1} # 类型检查器警告可选字段:
from typing import TypedDict
class UserDict(TypedDict, total=False):
"""所有字段都是可选的"""
id: int
name: str
email: str
# 可以只有部分字段
user: UserDict = {"id": 1} # 正确混合必需和可选:
from typing import TypedDict
class UserDict(TypedDict):
"""必需字段"""
id: int
name: str
class UserDictFull(UserDict, total=False):
"""继承并添加可选字段"""
email: str | None
age: int | None
# 使用
user1: UserDictFull = {"id": 1, "name": "张三"} # 正确
user2: UserDictFull = {
"id": 1,
"name": "张三",
"email": "test@example.com",
"age": 25
}3.5 渐进复杂
ReadOnly(Python 3.11+):
from typing import TypedDict, ReadOnly
class ConfigDict(TypedDict):
host: ReadOnly[str] # 不可修改
port: ReadOnly[int] # 不可修改
debug: bool # 可修改
config: ConfigDict = {
"host": "localhost",
"port": 8080,
"debug": False
}
config["debug"] = True # 正确
# config["port"] = 9000 # 类型检查器警告:ReadOnly3.6 实际应用
API 响应类型:
from typing import TypedDict
class APIResponse(TypedDict):
status: str
code: int
message: str
class UserData(TypedDict):
id: int
name: str
email: str
class UserResponse(APIResponse):
data: UserData
def get_user_response(user_id: int) -> UserResponse:
return {
"status": "success",
"code": 200,
"message": "获取成功",
"data": {
"id": user_id,
"name": "张三",
"email": "test@example.com"
}
}
response = get_user_response(1)
print(response["data"]["name"]) # "张三"3.7 TypedDict 最佳实践
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| API 响应用 TypedDict | 精确标注每个字段 | class APIResponse(TypedDict): |
必需字段不加 total=False | 默认行为更安全 | class User(TypedDict): id: int |
| 继承扩展可选字段 | 分层定义更清晰 | class UserFull(User, total=False): |
| ReadOnly 保护关键字段 | 防止意外修改 | id: ReadOnly[int] |
| 嵌套 TypedDict | 表达复杂结构 | data: UserData |
反模式
# ❌ 所有字段都可选
class User(TypedDict, total=False):
id: int # id 应该是必需的!
name: str
# ✅ 正确:必需字段单独定义
class User(TypedDict):
id: int # 必需
name: str # 必需
class UserOptional(User, total=False):
email: str # 可选# ❌ 用 dict[str, Any] 代替 TypedDict
user: dict[str, Any] = {"id": 1, "name": "张三"}
name = user["name"] # 类型是 Any,无 IDE 提示
# ✅ 正确:用 TypedDict
class UserDict(TypedDict):
id: int
name: str
user: UserDict = {"id": 1, "name": "张三"}
name = user["name"] # 类型是 str,有 IDE 提示# ❌ TypedDict 定义方法
class User(TypedDict):
id: int
name: str
def get_name(self) -> str: # TypedDict 不支持方法!
return self["name"]
# ✅ 正确:TypedDict 只定义字段,方法写在独立类
class User(TypedDict):
id: int
name: str
class UserEntity:
def __init__(self, data: User) -> None:
self.data = data
def get_name(self) -> str:
return self.data["name"]适用场景
| 场景 | 是否推荐 TypedDict | 原因 |
|---|---|---|
| API 响应/请求 | ✅ 推荐 | 字段固定,类型明确 |
| JSON 数据结构 | ✅ 推荐 | 精确标注每个字段 |
| 配置文件结构 | ✅ 推荐 | 字段名和类型清晰 |
| 数据库记录映射 | ✅ 推荐 | 字段类型固定 |
| 动态字段结构 | ❌ 用 dict[str, Any] | 字段不确定 |
| 需要方法/行为 | ❌ 用 dataclass | TypedDict 只存数据 |
| 需要默认值 | ❌ 用 dataclass | TypedDict 无默认值 |
| 需要验证逻辑 | ❌ 用 Pydantic | TypedDict 无验证 |
4. 运行时类型检查
4.1 问题引入
场景: 类型提示默认不运行时检查,如何验证参数类型?
def greet(name: str) -> str:
return f"Hello, {name}"
# 类型提示不阻止错误
greet(123) # 运行成功(虽然类型不对)
# 问题:如何在运行时验证类型?4.2 概念解释
get_type_hints: 获取函数的类型注解信息。
┌─────────────────────────────────────────────────────┐
│ 运行时类型检查语法 │
├─────────────────────────────────────────────────────┤
│ │
│ 获取类型提示: │
│ ─────────────────────────────────────── │
│ get_type_hints(func) │
│ → 返回 {'name': str, 'age': int, 'return': str} │
│ │
│ 解析泛型类型: │
│ ─────────────────────────────────────── │
│ get_origin(list[int]) → list │
│ get_args(list[int]) → (int,) │
│ get_origin(dict[str, int]) → dict │
│ get_args(dict[str, int]) → (str, int) │
│ │
│ 使用示例 → 解释: │
│ hints['name'] → 获取 name 参数的类型 │
│ hints['return'] → 获取返回值类型 │
│ get_origin(x) → 获取泛型的"外壳"(如 list) │
│ get_args(x) → 获取泛型的"内容"(如 int) │
│ │
│ 应用场景: │
│ • 运行时参数验证 │
│ • 动态类型检查 │
│ • API 数据验证 │
│ │
└─────────────────────────────────────────────────────┘4.3 最简示例
from typing import get_type_hints
def greet(name: str, age: int) -> str:
return f"Hello {name}, you are {age}"
# 获取类型提示
hints = get_type_hints(greet)
print(hints)
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
# 查看具体类型
print(f"name 参数: {hints['name']}") # str
print(f"age 参数: {hints['age']}") # int
print(f"返回值: {hints['return']}") # str4.4 详细说明
手动验证装饰器:
from typing import get_type_hints, get_origin, get_args, Union
import functools
def validate_args(func):
"""运行时参数类型验证"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
hints = get_type_hints(func)
# 检查参数
for i, (arg_name, arg_value) in enumerate(zip(hints.keys(), args)):
if arg_name == 'return':
continue
expected_type = hints[arg_name]
if not isinstance(arg_value, expected_type):
raise TypeError(
f"参数 '{arg_name}' 类型错误: "
f"期望 {expected_type.__name__}, 实际 {type(arg_value).__name__}"
)
return func(*args, **kwargs)
return wrapper
@validate_args
def add(a: int, b: int) -> int:
return a + b
# 测试
print(add(1, 2)) # 3
# add("1", 2) # TypeError: 参数 'a' 类型错误4.5 渐进复杂
完整类型检查器:
from typing import get_type_hints, get_origin, get_args, Union
from dataclasses import dataclass
class TypeChecker:
"""类型检查工具"""
@staticmethod
def check(value: any, expected: any) -> bool:
"""检查单个值"""
# None 检查
if expected is type(None):
return value is None
# Union 类型
origin = get_origin(expected)
if origin is Union:
args = get_args(expected)
return isinstance(value, args) or value is None
# 泛型容器
if origin:
return isinstance(value, origin)
# 基本类型
return isinstance(value, expected)
@classmethod
def validate(cls, obj: any, hints: dict) -> dict[str, list[str]]:
"""验证对象字段"""
errors: dict[str, list[str]] = {}
for field_name, expected_type in hints.items():
if hasattr(obj, field_name):
value = getattr(obj, field_name)
if not cls.check(value, expected_type):
errors[field_name] = [
f"期望 {expected_type}, 得到 {type(value).__name__}"
]
return errors
# 使用
@dataclass
class User:
name: str
age: int
email: str | None = None
user = User(name="张三", age=25)
hints = {"name": str, "age": int, "email": str | None}
errors = TypeChecker.validate(user, hints)
if errors:
print("验证失败:", errors)
else:
print("验证通过")4.6 实际应用
API 参数验证:
from typing import get_type_hints, TypedDict
class RequestData(TypedDict):
user_id: int
action: str
def validate_request(data: dict) -> RequestData:
"""验证请求数据"""
required_fields = {"user_id": int, "action": str}
for field, expected_type in required_fields.items():
if field not in data:
raise ValueError(f"缺少必需字段: {field}")
if not isinstance(data[field], expected_type):
raise TypeError(
f"字段 '{field}' 类型错误: "
f"期望 {expected_type.__name__}"
)
return data
def process_request(data: RequestData) -> str:
"""处理请求"""
return f"处理用户 {data['user_id']} 的 {data['action']} 请求"
# 使用
valid_data = validate_request({"user_id": 1, "action": "login"})
result = process_request(valid_data)
print(result) # "处理用户 1 的 login 请求"4.7 运行时类型检查最佳实践
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 简单验证用 isinstance | 内置函数,最快 | isinstance(x, int) |
| API 边界验证数据 | 防止外部错误数据 | validate_request(data) |
| 验证装饰器复用逻辑 | 一处定义,多处使用 | @validate_args |
| 使用 Pydantic 替代复杂验证 | 功能强大,生态完善 | BaseModel 类 |
| 避免递归检查大容器 | 性能开销大 | 只检查外壳类型 |
反模式
# ❌ 类型提示误认为运行时检查
def add(a: int, b: int) -> int:
return a + b
add("1", "2") # 运行正常!类型提示不阻止执行
# ✅ 正确:需要验证时手动检查或用 Pydantic
def add(a: int, b: int) -> int:
if not isinstance(a, int) or not isinstance(b, int):
raise TypeError("参数必须是整数")
return a + b# ❌ 完整验证所有嵌套元素(性能差)
def validate_every_element(data: list[list[int]]) -> bool:
for row in data:
for elem in row:
if not isinstance(elem, int):
return False
return True
# ✅ 正确:只验证外壳,信任内部类型(或用 Pydantic)
def validate_structure(data: list[list[int]]) -> bool:
return isinstance(data, list) and all(isinstance(row, list) for row in data)# ❌ 用 get_type_hints 过度验证内部函数
def internal_helper(x: int) -> str:
return str(x)
# 内部函数不需要运行时验证
# 类型检查器静态分析就够了
# ✅ 正确:只在边界(API入口)验证
def api_endpoint(data: dict[str, Any]) -> Result:
# API 入口验证
if not isinstance(data.get("id"), int):
raise ValueError("id 必须是整数")
# 内部逻辑信任类型
return process(data)适用场景
| 场景 | 是否推荐运行时验证 | 原因 |
|---|---|---|
| API 入口数据 | ✅ 推荐 | 外部数据不可信 |
| 用户输入验证 | ✅ 推荐 | 防止错误输入 |
| 配置文件加载 | ✅ 推荐 | 验证配置正确性 |
| 内部函数调用 | ❌ 不推荐 | 类型检查器已静态分析 |
| 性能敏感场景 | ❌ 不推荐 | 验证有开销 |
| 复杂嵌套结构 | 用 Pydantic | 手写验证复杂易错 |
替代方案:Pydantic(推荐用于生产)
from pydantic import BaseModel, Field
class User(BaseModel):
id: int = Field(gt=0) # 验证:必须大于 0
name: str = Field(min_length=1) # 验证:至少 1 字符
email: str | None = None
# 自动验证
user = User(id=1, name="张三")
# user = User(id=-1, name="") # ValidationError
# Pydantic 优势:
# • 自动运行时验证
# • 类型转换("123" → 123)
# • 详细错误信息
# • JSON 序列化/反序列化
# • 生态完善(FastAPI 集成)5. 类型守卫
5.1 问题引入
场景: 检查了类型后,类型检查器仍然不知道类型已收窄。
def process(items: list[object]) -> str:
# 检查是否全是字符串
if all(isinstance(x, str) for x in items):
# 类型检查器仍然认为 items 是 list[object]
return " ".join(items) # 类型检查器可能警告
return "不是字符串列表"问题: 如何告诉类型检查器"检查后类型已收窄"?
5.2 概念解释
TypeGuard: 标注类型检查函数,告诉类型检查器类型已收窄。
┌─────────────────────────────────────────────────────┐
│ TypeGuard 语法结构 │
├─────────────────────────────────────────────────────┤
│ │
│ 定义类型守卫函数: │
│ ─────────────────────────────────────── │
│ def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
│ ↑ │
│ 返回类型守卫 │
│ return all(isinstance(x, str) for x in val) │
│ │
│ 使用示例 → 解释: │
│ if is_str_list(items): │
│ # items 类型从 list[object] 收窄为 list[str] │
│ return " ".join(items) # 类型检查器认可 │
│ │
│ 核心特点: │
│ • 返回 True → 类型收窄为具体类型 │
│ • 返回 False → 类型保持不变 │
│ • 用于复杂类型检查(isinstance 无法处理的) │
│ │
│ 适用场景: │
│ • 检查列表元素类型 │
│ • 检查字典字段类型 │
│ • 检查嵌套结构类型 │
│ │
└─────────────────────────────────────────────────────┘5.3 最简示例
from typing import TypeGuard
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
"""类型守卫:检查是否为字符串列表"""
return all(isinstance(x, str) for x in val)
def process(items: list[object]) -> str:
if is_string_list(items):
# 类型检查器知道 items 是 list[str]
return " ".join(items) # 正确!
return "不是字符串列表"
# 使用
print(process(["a", "b", "c"])) # "a b c"
print(process([1, "b", 3])) # "不是字符串列表"5.4 详细说明
TypeGuard vs isinstance:
from typing import TypeGuard
def process(value: int | str) -> str:
# isinstance 自动收窄类型
if isinstance(value, int):
return f"整数: {value * 2}" # 类型检查器知道是 int
elif isinstance(value, str):
return f"字符串: {value.upper()}" # 类型检查器知道是 str
return ""
# TypeGuard 用于复杂类型检查
def is_positive_dict(val: dict[str, object]) -> TypeGuard[dict[str, int]]:
"""检查字典值是否为正整数"""
return all(isinstance(v, int) and v > 0 for v in val.values())
def sum_values(data: dict[str, object]) -> int:
if is_positive_dict(data):
# 类型检查器知道 data 是 dict[str, int]
return sum(data.values())
return 05.5 渐进复杂
自定义类型守卫:
from typing import TypeGuard, Any
def is_user_dict(val: dict[str, Any]) -> TypeGuard[dict[str, str | int]]:
"""检查是否为用户字典"""
required = {"id", "name"}
return (
all(k in val for k in required) and
isinstance(val.get("id"), int) and
isinstance(val.get("name"), str)
)
def process_user(data: dict[str, Any]) -> str:
if is_user_dict(data):
return f"用户 ID: {data['id']}, 姓名: {data['name']}"
return "无效用户数据"
# 使用
user_data = {"id": 1, "name": "张三", "email": "test@example.com"}
print(process_user(user_data)) # "用户 ID: 1, 姓名: 张三"5.6 实际应用
数据处理管道:
from typing import TypeGuard, Any
def is_valid_row(row: list[Any]) -> TypeGuard[list[str | int]]:
"""检查数据行是否有效"""
return len(row) == 3 and isinstance(row[0], str)
def is_complete_row(row: list[str | int]) -> TypeGuard[list[str | int | float]]:
"""检查数据行是否完整"""
return all(isinstance(v, (str, int, float)) for v in row)
def process_data(rows: list[list[Any]]) -> list[str]:
results: list[str] = []
for row in rows:
if is_valid_row(row):
if is_complete_row(row):
name, count, score = row
results.append(f"{name}: {count} 条, 平均 {score}")
else:
results.append(f"{row[0]}: 数据不完整")
else:
results.append("无效行")
return results
# 使用
data = [
["张三", 10, 85.5],
["李四", 5],
[123]
]
results = process_data(data)
for r in results:
print(r)关键代码说明:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
-> TypeGuard[list[str | int]] | 返回类型守卫而非 bool | TypeGuard 告知类型检查器:函数返回 True 时,参数类型被收窄为指定类型 |
if is_valid_row(row): 后可访问 row[0] | 类型守卫使类型收窄 | 进入 if 块后,row 被收窄为 list[str | int],类型检查器不再报错 |
if is_complete_row(row): 嵌套守卫 | 二次收窄类型 | 两层守卫实现渐进式类型精化,内层代码可以安全解包 name, count, score = row |
name, count, score = row | 解包赋值 | 经过两层类型守卫后,可以安全解包并使用具体字段名,代码可读性更高 |
5.7 TypeGuard 最佳实践
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 复杂类型检查用 TypeGuard | isinstance 无法处理 | list[str]、dict[str, int] |
| 简单类型用 isinstance | 内置自动收窄 | if isinstance(x, int): |
| TypeGuard 函数命名 is_xxx | 表达"判断是否是" | is_string_list |
| 检查逻辑与类型一致 | 返回 True 时类型正确 | 检查所有元素 |
| 返回类型明确收窄结果 | 类型检查器理解 | -> TypeGuard[list[str]] |
反模式
# ❌ 简单类型使用 TypeGuard(没必要)
def is_int(val: object) -> TypeGuard[int]:
return isinstance(val, int)
# ✅ 正确:简单类型直接用 isinstance
if isinstance(value, int):
# 类型已自动收窄为 int
pass# ❌ TypeGuard 检查逻辑与返回类型不一致
def is_positive_list(val: list[object]) -> TypeGuard[list[int]]:
return all(isinstance(x, int) for x in val) # 没检查是否为正数!
# ✅ 正确:检查逻辑与类型一致
def is_positive_int_list(val: list[object]) -> TypeGuard[list[int]]:
return all(isinstance(x, int) and x > 0 for x in val)# ❌ TypeGuard 函数有副作用
def is_valid(data: dict) -> TypeGuard[ValidDict]:
data["validated"] = True # 不要修改数据!
return True
# ✅ 正确:TypeGuard 只检查,不修改
def is_valid(data: dict) -> TypeGuard[ValidDict]:
return "id" in data and isinstance(data["id"], int)# ❌ 忽略 False 分支的类型
def process(items: list[object]) -> str:
if is_string_list(items):
return " ".join(items) # items 是 list[str]
else:
return " ".join(items) # items 仍是 list[object],可能报错
# ✅ 正确:False 分支类型不变
def process(items: list[object]) -> str:
if is_string_list(items):
return " ".join(items)
else:
return "不是字符串列表" # 不使用 items 元素适用场景
| 场景 | 是否推荐 TypeGuard | 原因 |
|---|---|---|
| 检查列表元素类型 | ✅ 推荐 | isinstance 无法处理 |
| 检查字典值类型 | ✅ 推荐 | 需要遍历验证 |
| 检查嵌套结构 | ✅ 推荐 | 复杂类型收窄 |
| 检查单个类型 | ❌ 用 isinstance | 内置更简洁 |
| 检查 Union 类型 | ❌ 用 isinstance | 自动收窄 |
| 检查 Optional 类型 | ❌ 用 is None | 更简单 |
L2 实践层:用好
泛型设计最佳实践
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 使用有意义的类型变量名 | 提高代码可读性 | T = TypeVar('T') 用 T 表示通用类型 |
| 约束泛型范围 | 防止传入不合适类型 | Number = TypeVar('Number', int, float) |
| 泛型类方法保持类型一致 | 确保类型安全 | get() -> T 与 set(item: T) |
| 简单容器优先用内置泛型 | 无需额外导入 | list[T] 比 List[T] 更现代 |
| 复杂泛型使用类型别名 | 简化重复类型 | `Result[T] = T |
| 避免过度泛型化 | 简单场景用具体类型 | 只有 2 种类型不需要泛型 |
反模式:不要这样做
# ❌ 无意义的类型变量名
X = TypeVar('X') # X 是什么?
Y = TypeVar('Y') # Y 是什么?
# ✅ 正确做法:使用有意义的名称
Item = TypeVar('Item') # 表示列表元素
Key = TypeVar('Key') # 表示键
Value = TypeVar('Value') # 表示值# ❌ 泛型约束过于宽松
T = TypeVar('T') # 任何类型都可以
def multiply(a: T, b: T) -> T:
return a * b # 如果 T 是 str,a * b 可能不是预期行为
# ✅ 正确做法:约束为数值类型
Number = TypeVar('Number', int, float, complex)
def multiply(a: Number, b: Number) -> Number:
return a * b# ❌ 过度泛型化
class StringProcessor(Generic[T]):
def process(self, text: T) -> T:
return text
# 实际只处理字符串,泛型没有意义
# ✅ 正确做法:直接使用具体类型
class StringProcessor:
def process(self, text: str) -> str:
return text# ❌ 泛型类方法返回类型不一致
class Box(Generic[T]):
def get(self) -> Any: # 应该返回 T
return self._item
# ✅ 正确做法:保持类型一致
class Box(Generic[T]):
def get(self) -> T:
return self._itemProtocol 使用最佳实践
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 用 Protocol 定义接口契约 | 明确对象需要实现的方法 | class Drawable(Protocol): |
方法签名用 ... 空实现 | 表示只声明不实现 | def draw(self) -> None: ... |
| 组合多个 Protocol | 定义复杂接口 | class Shape(Drawable, Fillable, Protocol): |
| 使用 @runtime_checkable 需要时才加 | 运行时检查有性能开销 | @runtime_checkable class Serializable(Protocol): |
| Protocol 命名用形容词或能力 | 表达"能做什么" | Comparable, Iterable, Serializable |
反模式:不要这样做
# ❌ Protocol 方法写实现代码
class Drawable(Protocol):
def draw(self) -> None:
print("画图") # Protocol 不应有实现!
# ✅ 正确做法:只声明签名
class Drawable(Protocol):
def draw(self) -> None: ... # 空实现,只声明# ❌ Protocol 继承普通类
class Drawable(BaseClass, Protocol): # Protocol 不应继承非 Protocol 类
pass
# ✅ 正确做法:只继承 Protocol 或其他 Protocol
class Drawable(Protocol):
pass
class AdvancedDrawable(Drawable, Protocol):
pass# ❌ 不必要的 @runtime_checkable
@runtime_checkable
class Drawable(Protocol):
def draw(self) -> None: ...
# 如果不需要 isinstance 检查,加 @runtime_checkable 是冗余的
# ✅ 正确做法:只在需要运行时检查时添加
class Drawable(Protocol):
def draw(self) -> None: ...
# 需要运行时检查时
@runtime_checkable
class Serializable(Protocol):
def to_json(self) -> str: ...适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 定义插件接口 | ✅ 推荐 | 插件只需实现方法,无需继承基类 |
| 库的公共接口 | ✅ 推荐 | 用户代码可实现协议,无需导入基类 |
| 鸭子类型类型化 | ✅ 推荐 | 让鸭子类型有类型检查 |
| 已有类适配 | ✅ 推荐 | 无需修改已有类,自动符合协议 |
| 强制继承关系 | ❌ 不推荐 | 需要显式继承时用普通基类 |
| 需要共享实现 | ❌ 不推荐 | Protocol 不提供实现,用抽象基类 |
L3 专家层:深入
泛型底层实现
Python 如何实现泛型
泛型内部机制:
┌─────────────────────────────────────────────────────┐
│ TypeVar 创建 │
│ │
│ T = TypeVar('T') │
│ ├── 创建 TypeVar 实例 │
│ ├── 存储名称和约束 │
│ ├── 运行时只是一个标记对象 │
│ │
│ 泛型类定义 │
│ ─────────────────────────────────────── │
│ class Box(Generic[T]): │
│ ├── Generic.__class_getitem__ 被调用 │
│ ├── 创建 _GenericAlias 对象 │
│ ├── Box[int] 返回这个别名对象 │
│ │
│ 类型检查器处理 │
│ ─────────────────────────────────────── │
│ ├── 静态分析时替换类型变量 │
│ ├── Box[int] 中 T 替换为 int │
│ ├── 运行时不替换,只是标记 │
└─────────────────────────────────────────────────────┘演示代码
from typing import TypeVar, Generic, get_origin, get_args
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
# 查看泛型类的内部结构
print(Box[int]) # __main__.Box[int]
print(type(Box[int])) # typing._GenericAlias
# 解析泛型
print(get_origin(Box[int])) # <class '__main__.Box'>
print(get_args(Box[int])) # (<class 'int'>,)
# 运行时行为
box = Box(42) # Box[int] 的实例
print(type(box)) # <class '__main__.Box'>
# 注意:运行时类型是 Box,不是 Box[int]TypeVar 绑定机制
from typing import TypeVar
# 无约束 TypeVar
T = TypeVar('T')
# 可以是任何类型
# 约束 TypeVar
Number = TypeVar('Number', int, float)
# 只能是 int 或 float
# 有上界的 TypeVar
T_bound = TypeVar('T_bound', bound=SupportsInt)
# 必须是 SupportsInt 或其子类
# 查看约束
print(T.__constraints__) # ()
print(Number.__constraints__) # (int, float)
print(T_bound.__bound__) # <class 'SupportsInt'>运行时类型检查的实现
运行时检查流程:
┌─────────────────────────────────────────────────────┐
│ 手动实现运行时检查 │
│ │
│ 步骤: │
│ 1. 获取类型提示 │
│ hints = get_type_hints(func) │
│ │
│ 2. 解析泛型类型 │
│ origin = get_origin(expected_type) │
│ args = get_args(expected_type) │
│ │
│ 3. 执行类型检查 │
│ if origin: │
│ check_generic(value, origin, args) │
│ else: │
│ isinstance(value, expected_type) │
│ │
│ 限制: │
│ ├── 泛型运行时只检查"外壳" │
│ ├── list[int] 检查 value 是 list │
│ ├── 不检查元素是否都是 int │
│ ├── 完整检查需要递归验证 │
└─────────────────────────────────────────────────────┘实现完整运行时检查
from typing import get_origin, get_args, Union
import inspect
def check_type(value: object, expected: type) -> bool:
"""完整的运行时类型检查"""
# 处理 None 类型
if expected is type(None):
return value is None
# 处理 Union 类型
origin = get_origin(expected)
if origin is Union:
args = get_args(expected)
return any(check_type(value, arg) for arg in args)
# 处理泛型容器
if origin is not None:
# 检查外壳类型
if not isinstance(value, origin):
return False
# 递归检查元素类型
args = get_args(expected)
if origin is list:
return all(check_type(item, args[0]) for item in value)
elif origin is dict:
k_type, v_type = args
return all(
check_type(k, k_type) and check_type(v, v_type)
for k, v in value.items()
)
elif origin is tuple:
if len(args) == 2 and args[1] is ...:
# 可变长度元组 tuple[int, ...]
return all(check_type(item, args[0]) for item in value)
else:
# 固定长度元组
return len(value) == len(args) and all(
check_type(v, t) for v, t in zip(value, args)
)
return True
# 基本类型
return isinstance(value, expected)
# 测试
print(check_type([1, 2, 3], list[int])) # True
print(check_type([1, "a", 3], list[int])) # False
print(check_type({"a": 1}, dict[str, int])) # True
print(check_type({"a": "b"}, dict[str, int])) # False性能考量
| 操作 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| TypeVar 创建 | O(1) | O(1) | 创建标记对象 |
| 泛型类定义 | O(1) | O(1) | 创建类,存储类型参数 |
| 泛型实例化 | O(1) | O(1) | 不比普通类慢 |
| get_origin/get_args | O(1) | O(1) | 访问内部属性 |
| 完整运行时检查 | O(n) | O(1) | n 为元素数量,递归检查 |
| isinstance 单次检查 | O(1) | O(1) | 不检查泛型内部 |
设计动机
| 设计选择 | 原因 | 替代方案对比 |
|---|---|---|
| 泛型运行时不检查 | 保持性能,类型检查在静态分析时完成 | Java 泛型运行时擦除,类似设计 |
| TypeVar 是标记对象 | 简化实现,无需复杂类型系统 | Rust 泛型编译时展开,有运行时成本 |
| Protocol 结构化类型 | 鸭子类型的类型化,不强制继承 | Go 的接口类似,隐式实现 |
| Generic 基类 | 明确声明泛型类 | C++ 模板无需声明,编译器推断 |
知识关联
泛型知识关联:
┌───────────────┐
│ 类型检查器 │
│ 静态分析 │
└───────────────┘
│
↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ TypeVar │────→│ Generic │────→│ 泛型类 │
│ 类型变量 │ │ 泛型基类 │ │ Box[T] │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 约束 │ │ _GenericAlias│ │ 运行时检查 │
│ bound │ │ 内部对象 │ │ 手动实现 │
└───────────────┘ └───────────────┘ └───────────────┘本章总结(完整版)
核心技能速查
| 技能 | 说明 | 示例 |
|---|---|---|
| TypeVar | 类型变量 | T = TypeVar('T') |
| Generic | 泛型类 | class Stack(Generic[T]): |
| Protocol | 协议类型 | class Drawable(Protocol): |
| TypedDict | 字典类型 | class UserDict(TypedDict): |
| get_type_hints | 获取类型注解 | get_type_hints(func) |
| TypeGuard | 类型守卫 | -> TypeGuard[list[str]] |
三层学习路线
类型进阶学习路线:
├── L1 理解层(会用)
│ ├── 第 1 步:掌握泛型函数(TypeVar + Generic)
│ ├── 第 2 步:学会泛型类设计
│ ├── 第 3 步:理解 Protocol 协议
│ ├── 第 4 步:使用 TypedDict 精确标注字典
│ └── 第 5 步:实现 TypeGuard 类型守卫
│
├── L2 实践层(用好)
│ ├── 泛型约束范围
│ ├── Protocol 接口设计
│ ├── 类型别名简化复杂泛型
│ └── 避免过度泛型化
│
└── L3 专家层(深入)
├── 理解 _GenericAlias 内部结构
├── TypeVar 绑定机制
├── 运行时类型检查实现
└── 性能考量与设计动机