pin_drop当前位置:知识文库 ❯ 图文

Python typing.Generic详解 - 泛型类与类型安全容器完全指南

一、Generic 概述

Generictyping 模块中用于创建泛型类的基类。通过继承 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

参数说明

参数 类型 说明
*type_vars TypeVar 类型变量列表,定义类的类型参数

常见用法

用法 语法 说明
单类型参数 class C(Generic[T]) 类有一个类型参数
多类型参数 class C(Generic[K, V]) 类有多个类型参数
继承泛型类 class Sub(Generic[T], Base) 同时继承泛型和其他基类
具体化泛型 class Sub(Base[int]) 固定类型参数为具体类型

三、代码示例详解

示例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 定义可扩展的基类,让使用者通过指定类型参数来定制行为。


五、注意事项与最佳实践

注意1Generic 必须作为基类使用,不能直接实例化。Generic[T] 只是声明类型参数,实际的类型在子类实例化时确定。

注意2:泛型类可以同时继承多个基类,但 Generic 应放在最前面,如 class MyClass(Generic[T], BaseClass)

注意3:运行时 Box[int]Box[str] 是同一个类,Python 不会为不同的类型参数创建不同的类。类型参数信息仅在静态分析时使用。

提示:在 Python 3.12+ 中,可以使用更简洁的语法定义泛型类:class Box[T]:,无需显式使用 TypeVarGeneric


六、相关方法对比

对比项 Generic[T] ABC + Generic[T] 具体化继承 普通类
类型参数化 支持 支持 固定类型 不支持
抽象方法 不支持 支持 取决于基类 不支持
类型安全 最高
灵活性 最高
适用场景 通用容器 框架基类 特定类型容器 简单场景

七、小结与练习题

小结

  • Generic 是创建泛型类的基类,通过传入 TypeVar 实现类型参数化

  • 泛型类支持单类型参数和多类型参数,满足不同的设计需求

  • 泛型类可以继承和被继承,实现代码复用和类型安全的统一

  • Python 3.12+ 提供了更简洁的泛型类语法 class C[T]:

练习题

练习1

实现一个泛型类 LinkedList(Generic[T]),包含 appendprependremove__len__ 方法,支持任意元素类型。

练习2

实现一个泛型类 BiMap(Generic[K, V]),支持通过键查值和通过值查键的双向映射,要求键和值都唯一。

练习3

创建一个泛型基类 Repository(Generic[T]),定义 get_by_idsavedelete 等方法,然后创建 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: 的简洁语法,无需显式导入 TypeVarGeneric,代码更简洁,类型变量的作用域也更清晰。

标签: Python typing Generic 泛型类 类型安全 容器类 Repository模式

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python typing.TypeVar详解 - 类型变量与泛型编程完全指南 下一篇: Python typing.Protocol详解 - 结构化子类型与静态鸭子类型指南

poll相关推荐