Python 中的泛型(Generics)是类型系统的一部分,虽然它们在日常脚本编写中可能不常见,但在大型项目、库开发或需要严格类型检查的场景中非常有用。
1. 什么是泛型?
泛型(Generics)允许你定义参数化的类型,即一个类型可以接受其他类型作为参数。例如:
List[int]
表示“元素类型为int
的列表”。Dict[str, float]
表示“键为str
、值为float
的字典”。
泛型的核心目的是提高代码的类型安全性和可读性,同时支持灵活的静态类型检查。
2. Python 泛型的基础
Python 的泛型是通过 typing
模块实现的(Python 3.5+),主要分为两类:
- 容器泛型(如
List
、Dict
、Set
)。 - 自定义泛型(通过
TypeVar
或继承Generic
)。
示例 1:内置容器泛型
from typing import List, Dict
def process_numbers(numbers: List[int]) -> float:
return sum(numbers) / len(numbers)
data: List[int] = [1, 2, 3]
result = process_numbers(data) # 类型检查通过
示例 2:类型变量(TypeVar
)
from typing import TypeVar, Sequence
T = TypeVar('T') # 声明一个泛型类型变量
def first_element(items: Sequence[T]) -> T:
return items[0]
# 调用时,T 会自动推断为实际类型
print(first_element([1, 2, 3])) # T 是 int
print(first_element(["a", "b"])) # T 是 str
3. 为什么需要泛型?
场景 1:避免重复代码
假设你要写一个函数,既能处理 int
列表也能处理 str
列表:
from typing import Sequence, TypeVar
T = TypeVar('T')
def process(items: Sequence[T]) -> T:
return items[0]
# 无需为每种类型写单独的函数
process([1, 2, 3]) # 返回 int
process(["a", "b"]) # 返回 str
场景 2:明确容器内容的类型
from typing import Dict
# 明确表示键是 str,值是 int
scores: Dict[str, int] = {"Alice": 90, "Bob": 85}
def update_score(db: Dict[str, int], name: str, value: int) -> None:
db[name] = value
update_score(scores, "Alice", 95) # 类型检查通过
update_score(scores, 123, 95) # 类型检查会报错(键必须是 str)
4. 自定义泛型类
通过继承 Generic
,可以创建自己的泛型类:
from typing import Generic, TypeVar, List
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()
# 使用示例
int_stack = Stack[int]()
int_stack.push(1)
int_stack.push("hello") # 类型检查会报错(应为 int)
str_stack = Stack[str]()
str_stack.push("world")
5. 泛型的约束
可以用 bound
限制泛型类型的范围:
from typing import TypeVar, Number
N = TypeVar('N', bound=Number) # 只能是 Number 的子类(如 int, float)
def add(a: N, b: N) -> N:
return a + b
add(1, 2) # 正确
add(1.5, 3.2) # 正确
add("a", "b") # 类型检查报错
6. 动态类型 vs 静态类型中的泛型
- 动态类型:Python 运行时不会强制泛型约束(
List[int]
和List[str]
运行时是同一个类)。 - 静态类型检查:工具如
mypy
会根据泛型类型进行验证。
运行时行为
from typing import List
x: List[int] = [1, 2, 3]
x.append("hello") # 运行时不会报错,但 mypy 会标记为错误
7. 实际应用场景
-
API 设计:明确函数参数和返回值的类型关系。
def parse_response(response: Dict[str, Any]) -> Optional[User]: ...
-
库开发:如
pandas
的DataFrame
、numpy
的ndarray
可以用泛型标注。 -
团队协作:大型项目中减少类型相关的 bug。
8. 常见问题
Q1:泛型会影响性能吗?
不会。泛型仅在静态类型检查时起作用,运行时会被擦除(类似 Java 的泛型擦除)。
Q2:Python 2 能用泛型吗?
不能。泛型是 Python 3 的特性(需 typing
模块支持)。
Q3:泛型和 Union
有什么区别?
Union[int, str]
表示“可以是int
或str
”。List[T]
表示“所有元素都是类型T
”。
9. 总结
- 泛型的作用:让类型系统更灵活,同时保持安全性。
- 常用工具:
typing.List
、typing.Dict
、TypeVar
、Generic
。 - 适用场景:大型项目、库开发、需要静态类型检查时。
可以尝试用 mypy
检查以下代码:
from typing import List
def average(numbers: List[float]) -> float:
return sum(numbers) / len(numbers)
average([1, 2, "3"]) # mypy 会报错