pin_drop当前位置:知识文库 ❯ 图文
Python typing.Generic详解 - 泛型类与类型安全容器完全指南
一、Generic 概述
Generic 是 typing 模块中用于创建泛型类的基类。通过继承 Generic 并传入类型变量(TypeVar),你可以定义参数化的类——类的某些属性或方法的类型在实例化时才确定。
泛型类是实现类型安全容器、通用数据结构和框架代码的核心工具。它使得同一个类可以适用于多种类型,同时保持完整的类型信息,避免了使用 Any 导致的类型安全损失。
二、语法与参数说明
基本语法
代码示例
from typing import Generic, TypeVar
# 定义类型变量
T = TypeVar('T')
K = TypeVar('K')
V = TypeVar('V')
# 单类型参数的泛型类
class Container(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
# 多类型参数的泛型类
class Pair(Generic[K, V]):
def __init__(self, key: K, value: V) -> None:
self.key = key
self.value = value
# 继承泛型类
class IntContainer(Container[int]):
pass
参数说明
常见用法
三、代码示例详解
示例1:单类型参数泛型类
以下示例定义了一个泛型盒子类,可以存放任意类型的值,同时保持类型安全:
代码示例
from typing import Generic, TypeVar, List, Optional
T = TypeVar('T')
class Box(Generic[T]):
"""泛型盒子,存放一个值"""
def __init__(self, value: T) -> None:
self._value: T = value
def get(self) -> T:
"""获取值"""
return self._value
def set(self, value: T) -> None:
"""设置值"""
self._value = value
def __repr__(self) -> str:
return f"Box({self._value!r})"
# 使用整数盒子
int_box: Box[int] = Box(42)
print(f"整数盒子: {int_box}")
print(f"获取值: {int_box.get()}")
int_box.set(100)
print(f"设置后: {int_box.get()}")
# 使用字符串盒子
str_box: Box[str] = Box("hello")
print(f"\n字符串盒子: {str_box}")
print(f"获取值: {str_box.get()}")
输出:
代码示例
整数盒子: Box(42)
获取值: 42
设置后: 100
字符串盒子: Box('hello')
获取值: hello
示例2:多类型参数泛型类
以下示例定义了一个泛型字典类,键和值的类型完全独立:
代码示例
from typing import Generic, TypeVar, List, Optional
K = TypeVar('K')
V = TypeVar('V')
class Dictionary(Generic[K, V]):
"""泛型字典,键值对类型独立"""
def __init__(self) -> None:
self._data: List[tuple] = []
def put(self, key: K, value: V) -> None:
"""添加或更新键值对"""
for i, (k, _) in enumerate(self._data):
if k == key:
self._data[i] = (key, value)
return
self._data.append((key, value))
def get(self, key: K) -> Optional[V]:
"""根据键获取值"""
for k, v in self._data:
if k == key:
return v
return None
def remove(self, key: K) -> bool:
"""删除键值对"""
for i, (k, _) in enumerate(self._data):
if k == key:
self._data.pop(i)
return True
return False
def keys(self) -> List[K]:
"""获取所有键"""
return [k for k, _ in self._data]
def items(self) -> List[tuple]:
"""获取所有键值对"""
return self._data.copy()
# 字符串键,整数值
scores: Dictionary[str, int] = Dictionary()
scores.put("Alice", 95)
scores.put("Bob", 87)
scores.put("Charlie", 92)
print("成绩字典:")
for key, value in scores.items():
print(f" {key}: {value}")
print(f"\nAlice 的成绩: {scores.get('Alice')}")
print(f"David 的成绩: {scores.get('David')}")
# 整数键,字符串值
names: Dictionary[int, str] = Dictionary()
names.put(1, "Alice")
names.put(2, "Bob")
print(f"\n编号1的姓名: {names.get(1)}")
输出:
代码示例
成绩字典:
Alice: 95
Bob: 87
Charlie: 92
Alice 的成绩: 95
David 的成绩: None
编号1的姓名: Alice
示例3:继承泛型类
泛型类可以被继承,子类可以继续保持泛型特性,也可以将类型参数具体化:
代码示例
from typing import Generic, TypeVar, List, Iterator
T = TypeVar('T')
class Collection(Generic[T]):
"""泛型集合基类"""
def __init__(self) -> None:
self._items: List[T] = []
def add(self, item: T) -> None:
self._items.append(item)
def size(self) -> int:
return len(self._items)
def is_empty(self) -> bool:
return len(self._items) == 0
def __iter__(self) -> Iterator[T]:
return iter(self._items)
class SortedCollection(Collection[T]):
"""排序集合,继承泛型类"""
def __init__(self) -> None:
super().__init__()
def add(self, item: T) -> None:
"""添加元素并保持排序"""
self._items.append(item)
self._items.sort()
def min(self) -> T:
"""获取最小值"""
if self.is_empty():
raise ValueError("集合为空")
return self._items[0]
def max(self) -> T:
"""获取最大值"""
if self.is_empty():
raise ValueError("集合为空")
return self._items[-1]
class UniqueCollection(Collection[T]):
"""去重集合,继承泛型类"""
def __init__(self) -> None:
super().__init__()
self._seen: set = set()
def add(self, item: T) -> None:
"""添加元素,自动去重"""
if item not in self._seen:
self._items.append(item)
self._seen.add(item)
# 使用排序集合
sorted_nums: SortedCollection[int] = SortedCollection()
for num in [5, 2, 8, 1, 9, 3]:
sorted_nums.add(num)
print(f"排序集合: {list(sorted_nums)}")
print(f"最小值: {sorted_nums.min()}, 最大值: {sorted_nums.max()}")
# 使用去重集合
unique_words: UniqueCollection[str] = UniqueCollection()
for word in ["apple", "banana", "apple", "cherry", "banana"]:
unique_words.add(word)
print(f"\n去重集合: {list(unique_words)}")
print(f"元素数量: {unique_words.size()}")
输出:
代码示例
排序集合: [1, 2, 3, 5, 8, 9]
最小值: 1, 最大值: 9
去重集合: ['apple', 'banana', 'cherry']
元素数量: 3
四、实际应用场景
-
自定义容器类:当内置容器类型无法满足需求时,可以使用
Generic创建自定义容器,如优先队列、双向映射字典等,同时保持类型安全。 -
Repository 模式:在数据访问层中,使用泛型 Repository 类
Repository[T],为不同的实体类型提供统一的 CRUD 操作接口。 -
框架与库开发:在开发通用框架或库时,使用
Generic定义可扩展的基类,让使用者通过指定类型参数来定制行为。
五、注意事项与最佳实践
注意1:
Generic必须作为基类使用,不能直接实例化。Generic[T]只是声明类型参数,实际的类型在子类实例化时确定。
注意2:泛型类可以同时继承多个基类,但
Generic应放在最前面,如class MyClass(Generic[T], BaseClass)。
注意3:运行时
Box[int]和Box[str]是同一个类,Python 不会为不同的类型参数创建不同的类。类型参数信息仅在静态分析时使用。
提示:在 Python 3.12+ 中,可以使用更简洁的语法定义泛型类:
class Box[T]:,无需显式使用TypeVar和Generic。
六、相关方法对比
七、小结与练习题
小结
-
Generic是创建泛型类的基类,通过传入TypeVar实现类型参数化 -
泛型类支持单类型参数和多类型参数,满足不同的设计需求
-
泛型类可以继承和被继承,实现代码复用和类型安全的统一
-
Python 3.12+ 提供了更简洁的泛型类语法
class C[T]:
练习题
练习1
实现一个泛型类 LinkedList(Generic[T]),包含 append、prepend、remove 和 __len__ 方法,支持任意元素类型。
练习2
实现一个泛型类 BiMap(Generic[K, V]),支持通过键查值和通过值查键的双向映射,要求键和值都唯一。
练习3
创建一个泛型基类 Repository(Generic[T]),定义 get_by_id、save、delete 等方法,然后创建 UserRepository(Repository[User]) 具体化子类。
常见问题
Generic 和普通继承有什么区别?
普通继承的父类是固定的具体类型,而 Generic 的父类带有类型变量参数,这个参数在子类实例化时才确定。这使得泛型类可以在不同场景下复用同一套代码,同时保持类型安全。
运行时能获取到泛型类的类型参数吗?
在 Python 3.8 及以下版本中,运行时无法获取类型参数。从 Python 3.9 开始,通过 __class_getitem__ 可以在一定程度上获取,但类型参数信息主要用于静态分析,不应在运行时依赖。
泛型类可以继承多个 Generic 吗?
可以。你可以写 class MyClass(Generic[T], BaseClass1, BaseClass2)。但注意 Generic 应该放在继承列表的最前面。每个类型变量只需要在 Generic 中声明一次。
Python 3.12 的新泛型语法有什么优势?
Python 3.12 的 PEP 695 引入了 class Box[T]: 和 def func[T](x: T) -> T: 的简洁语法,无需显式导入 TypeVar 和 Generic,代码更简洁,类型变量的作用域也更清晰。
本文涉及AI创作
内容由AI创作,请仔细甄别