本篇文件介绍 Python 中的 泛型(Generics)

Intro

在没有泛型的情况下, 会遇上以下几个问题:

  1. 难以表达意图
    假设你编写了一个函数, 它接受一个列表, 并返回列表中的第一个元素. 在不使用类型提示的情况下, 这个函数可以处理任何类型的列表, 但我们无法在函数签名中表达"返回的元素的类型与列表中的元素类型相同"这个意图

    PYTHON
    def get_first_element(items):
        return items[0]
    Click to expand and view more
  2. 丧失类型信息
    如果使用类型提示, 可能会像下面这样写, 但这样会丢失类型信息. list[Any] 表示可以接收任何类型的列表, 但 -> Any 意味着不知道返回的元素类型是什么, 这使得 mypy 等静态类型检测工具无法追踪类型, 降低了代码的可读性和安全性

    PYTHON
    from typing import Any
    def get_first_element(items: list[Any]) -> Any:
        return items[0]
    
    # 调用时, 类型检查工具无法得知 first_str 的类型
    first_str = get_first_element(["hello", "world"])
    Click to expand and view more
  3. 代码重复
    如果为每种可能的类型都编写一个单独的函数, 则会导致代码重复

    PYTHON
    def get_first_int(items: list[int]) -> int:
        return items[0]
    
    def get_first_str(items: list[str]) -> str:
        return items[0]
    Click to expand and view more

通过引入 类型变量 (TypeVar) 来解决问题, 类型变量就像一个占位符, 代表在未来某时刻会被具体指定的类型

PYTHON
from typing import TypeVar

T = TypeVar("T")

def get_first_element(items: list[T]) -> T:
    return items[0]

# 现在, 类型检查工具可以正确推断出类型
first_str: str = get_first_element(["hello", "world"])
first_int: int = get_first_element([1, 2, 3])
Click to expand and view more

注意: 这个函数假设列表非空, 如果传入空列表会抛出 IndexError

Generic Class

除了函数, 泛型也常用于定义泛型类

PYTHON
from typing import TypeVar, Generic

T = TypeVar("T")

class Box(Generic[T]):
    def __init__(self, items: list[T]):
        self._items = items

    def get(self) -> T:
        return self._items[0]

    def add(self, item: T) -> None:
        self._items.append(item)

# 创建一个存储字符串的 Box
string_box = Box(["apple", "banana"])
item_str = string_box.get()  # str
string_box.add("cherry")

# 创建一个存储整数的 Box
int_box = Box([10, 20])
item_int = int_box.get()  # int
int_box.add(30)
Click to expand and view more

Advanced Usage

简单介绍一下泛型的一些进阶用法

Wrapping Up

泛型是 Python 类型提示系统中一个非常强大的工具, 它通过类型变量帮助我们编写更加灵活、安全且可维护的代码.

它虽然不会影响程序的运行时行为 (类型信息在运行时会被擦除), 但它为静态类型分析提供了必要的信息, 使得代码意图更加清晰, 并且能在早期发现类型错误.

Python 的泛型是类型提示系统的一部分, 和 C++/Java 的编译期泛型不同, 它的作用主要是:

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut