导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • Runnable
  • PromptEngineering
  • dataclasses
  • LaTeX
  • rank_bm25
  • TF-IDF
  • asyncio
  • sqlalchemy
  • fastapi
  • Starlette
  • uvicorn
  • argparse
  • Generic
  • ssl
  • urllib
  • python-dotenv
  • RRF
  • CrossEncoder
  • Lost-in-the-middle
  • Jinja2
  • logger
  • io
  • venv
  • concurrent
  • parameter
  • SSE
  • 1. Generic 和 TypeVar 是什么?
    • 1.1 为什么需要泛型?
  • 2. 前置知识
    • 2.1 Python 类型提示基础
    • 2.2 typing 模块
  • 3. TypeVar:定义类型变量
    • 3.1 基本用法
    • 3.2 在函数中使用 TypeVar
    • 3.3 类型约束:限制类型变量的范围
  • 4. Generic:创建泛型类
    • 4.1 基本用法
    • 4.2 泛型栈示例
    • 4.3 多个类型参数
  • 5. 实际应用示例
    • 5.1 示例1:API 响应包装器
    • 5.2 示例2:带约束的泛型类
    • 5.3 示例3:简单的链表
  • 6. 常见问题
    • 6.1 什么时候使用泛型?
    • 6.2 TypeVar 和 Generic 的区别?
    • 6.3 类型检查器会检查泛型吗?
    • 6.4 泛型会影响运行时性能吗?
  • 7. 总结
    • 7.1 核心概念回顾
    • 7.2 使用泛型的好处

1. Generic 和 TypeVar 是什么? #

Generic(泛型) 和 TypeVar(类型变量) 是 Python typing 模块提供的功能,用于创建可以处理多种类型的通用代码,同时保持类型安全。

1.1 为什么需要泛型? #

想象一下,如果你要写一个函数来获取列表的第一个元素:

# 没有泛型:需要为每种类型写一个函数
def get_first_int(lst: list[int]) -> int:
    return lst[0]

def get_first_str(lst: list[str]) -> str:
    return lst[0]

def get_first_float(lst: list[float]) -> float:
    return lst[0]

这样写代码重复,而且类型不明确。使用泛型后:

# 使用泛型:一个函数处理所有类型
from typing import TypeVar, List

T = TypeVar('T')  # T 表示"某种类型"

def get_first(lst: List[T]) -> T:
    return lst[0]

# 可以用于任何类型
first_int = get_first([1, 2, 3])      # 返回 int
first_str = get_first(["a", "b", "c"])  # 返回 str
// TypeScript 泛型写法
function getFirst<T>(lst: T[]): T {
    return lst[0];
}

// 可以用于任何类型
const firstInt = getFirst([1, 2, 3]);          // number
const firstStr = getFirst(["a", "b", "c"]);    // string

泛型的好处:

  1. 代码复用:一个函数/类可以处理多种类型
  2. 类型安全:类型检查器可以验证类型是否正确
  3. 代码清晰:明确表达"这个函数可以处理任何类型"
  4. IDE 支持:更好的自动完成和类型提示

2. 前置知识 #

在学习 Generic 和 TypeVar 之前,你需要了解以下基础知识。

2.1 Python 类型提示基础 #

Python 3.5+ 支持类型提示,用于标注变量、函数参数和返回值的类型。

# 基本类型提示
name: str = "张三"        # 变量 name 是字符串类型
age: int = 25             # 变量 age 是整数类型
price: float = 99.9       # 变量 price 是浮点数类型

# 函数类型提示
def add(a: int, b: int) -> int:
    # a 和 b 是整数类型,返回值也是整数类型
    return a + b

# 列表类型提示
numbers: list[int] = [1, 2, 3]  # Python 3.9+
# 或者
from typing import List
numbers: List[int] = [1, 2, 3]   # Python 3.9 以下

2.2 typing 模块 #

typing 模块提供了更多类型提示工具,如 List、Dict、Optional 等。

# 导入 typing 模块中的类型
from typing import List, Dict, Optional

# List[T] 表示元素类型为 T 的列表
numbers: List[int] = [1, 2, 3]

# Dict[K, V] 表示键类型为 K、值类型为 V 的字典
scores: Dict[str, int] = {"张三": 90, "李四": 85}

# Optional[T] 表示类型 T 或 None
name: Optional[str] = None  # name 可以是字符串或 None

如果你对类型提示不熟悉,建议先学习 Python 类型提示的基础知识。

3. TypeVar:定义类型变量 #

TypeVar(类型变量) 用于定义一个"占位符"类型,表示"某种类型",但在定义时还不确定具体是什么类型。

3.1 基本用法 #

# 导入 TypeVar
from typing import TypeVar

# 创建类型变量 T
# T 可以是任何类型(int、str、float、自定义类等)
T = TypeVar('T')

# 创建另一个类型变量 S
S = TypeVar('S')

说明:

  • TypeVar('T') 创建一个名为 T 的类型变量
  • T 表示"某种类型",可以是任何类型
  • 类型变量的名字通常使用单个大写字母(如 T、K、V)

3.2 在函数中使用 TypeVar #

# 导入 TypeVar 和 List
from typing import TypeVar, List

# 定义类型变量 T
T = TypeVar('T')

# 定义函数:获取列表的第一个元素
# List[T] 表示元素类型为 T 的列表
# -> T 表示返回值类型也是 T
def first_element(lst: List[T]) -> T:
    # 返回列表的第一个元素
    # 返回类型与列表元素类型相同
    return lst[0]

# 主程序入口
if __name__ == "__main__":
    # 使用整数列表
    # T 被推断为 int
    numbers: List[int] = [1, 2, 3]
    result: int = first_element(numbers)
    print(f"第一个数字:{result},类型:{type(result).__name__}")

    # 使用字符串列表
    # T 被推断为 str
    strings: List[str] = ["a", "b", "c"]
    result2: str = first_element(strings)
    print(f"第一个字符串:{result2},类型:{type(result2).__name__}")

    # 使用浮点数列表
    # T 被推断为 float
    floats: List[float] = [1.1, 2.2, 3.3]
    result3: float = first_element(floats)
    print(f"第一个浮点数:{result3},类型:{type(result3).__name__}")

运行结果:

第一个数字:1,类型:int
第一个字符串:a,类型:str
第一个浮点数:1.1,类型:float

说明:

  • 同一个函数可以处理不同类型的列表
  • 类型检查器知道返回值的类型与列表元素类型相同
  • 如果传入 List[int],返回值就是 int;如果传入 List[str],返回值就是 str

3.3 类型约束:限制类型变量的范围 #

有时候,你希望类型变量只能是某些特定类型,可以使用类型约束。

# 导入 TypeVar
from typing import TypeVar

# 定义只能接受数字类型的类型变量
# Number 只能是 int 或 float
Number = TypeVar('Number', int, float)

# 定义函数:计算两个数字的和
def add_numbers(a: Number, b: Number) -> Number:
    # 返回两个数字的和
    return a + b

# 主程序入口
if __name__ == "__main__":
    # 使用整数(允许)
    result1 = add_numbers(1, 2)
    print(f"整数相加:{result1}")

    # 使用浮点数(允许)
    result2 = add_numbers(1.5, 2.5)
    print(f"浮点数相加:{result2}")

    # 使用字符串(不允许,类型检查器会报错)
    # result3 = add_numbers("1", "2")  # 类型错误!

说明:

  • TypeVar('Number', int, float) 表示 Number 只能是 int 或 float
  • 如果传入其他类型(如 str),类型检查器会报错
  • 这样可以确保函数只接受特定类型的参数

4. Generic:创建泛型类 #

Generic(泛型类) 用于创建可以接受类型参数的类,让类可以处理多种类型的数据。

4.1 基本用法 #

# 导入 TypeVar 和 Generic
from typing import TypeVar, Generic

# 定义类型变量 T
T = TypeVar('T')

# 定义泛型类 Box
# Generic[T] 表示这个类接受一个类型参数 T
class Box(Generic[T]):
    """一个可以存放任何类型值的盒子"""

    def __init__(self, value: T):
        # 初始化方法,接受类型为 T 的值
        self.value = value

    def get(self) -> T:
        # 获取值,返回类型为 T
        return self.value

    def set(self, new_value: T) -> None:
        # 设置值,参数类型为 T
        self.value = new_value

# 主程序入口
if __name__ == "__main__":
    # 创建存放整数的盒子
    # Box[int] 表示这个盒子存放整数
    int_box: Box[int] = Box[int](42)
    print(f"整数盒子:{int_box.get()}")

    # 创建存放字符串的盒子
    # Box[str] 表示这个盒子存放字符串
    str_box: Box[str] = Box[str]("Hello")
    print(f"字符串盒子:{str_box.get()}")

    # 创建存放列表的盒子
    # Box[list] 表示这个盒子存放列表
    list_box: Box[list] = Box[list]([1, 2, 3])
    print(f"列表盒子:{list_box.get()}")

运行结果:

整数盒子:42
字符串盒子:Hello
列表盒子:[1, 2, 3]

说明:

  • Generic[T] 表示这个类接受一个类型参数 T
  • Box[int] 表示存放整数的盒子
  • Box[str] 表示存放字符串的盒子
  • 类型检查器可以验证类型是否正确
// 定义泛型类 Box
class Box<T> {
    // 用于存放任何类型值的盒子
    private value: T;

    constructor(value: T) {
        // 初始化方法,接受类型为 T 的值
        this.value = value;
    }

    get(): T {
        // 获取值,返回类型为 T
        return this.value;
    }

    set(newValue: T): void {
        // 设置值,参数类型为 T
        this.value = newValue;
    }
}
// 主程序入口
// 创建存放整数的盒子
// Box<number> 表示这个盒子存放整数
const intBox: Box<number> = new Box<number>(42);
console.log(`整数盒子:${intBox.get()}`);

// 创建存放字符串的盒子
// Box<string> 表示这个盒子存放字符串
const strBox: Box<string> = new Box<string>("Hello");
console.log(`字符串盒子:${strBox.get()}`);

// 创建存放列表的盒子
// Box<number[]> 表示这个盒子存放数字数组
const listBox: Box<number[]> = new Box<number[]>([1, 2, 3]);
console.log(`列表盒子:${listBox.get()}`);

4.2 泛型栈示例 #

# 导入 TypeVar、Generic 和 Optional
from typing import TypeVar, Generic, Optional, List

# 定义类型变量 T
T = TypeVar('T')

# 定义泛型栈类
class Stack(Generic[T]):
    """泛型栈:可以存放任何类型的元素"""

    def __init__(self) -> None:
        # 初始化空栈
        # items 是元素类型为 T 的列表
        self.items: List[T] = []

    def push(self, item: T) -> None:
        # 入栈:将元素添加到栈顶
        self.items.append(item)

    def pop(self) -> Optional[T]:
        # 出栈:移除并返回栈顶元素
        # 如果栈为空,返回 None
        return self.items.pop() if self.items else None

    def peek(self) -> Optional[T]:
        # 查看栈顶元素(不移除)
        # 如果栈为空,返回 None
        return self.items[-1] if self.items else None

    def is_empty(self) -> bool:
        # 检查栈是否为空
        return len(self.items) == 0

    def size(self) -> int:
        # 返回栈的大小
        return len(self.items)

# 主程序入口
if __name__ == "__main__":
    # 创建整数栈
    int_stack: Stack[int] = Stack()
    int_stack.push(1)
    int_stack.push(2)
    int_stack.push(3)
    print(f"整数栈大小:{int_stack.size()}")
    print(f"栈顶元素:{int_stack.peek()}")
    print(f"出栈:{int_stack.pop()}")
    print(f"出栈后大小:{int_stack.size()}")

    print("-" * 30)

    # 创建字符串栈
    str_stack: Stack[str] = Stack()
    str_stack.push("hello")
    str_stack.push("world")
    str_stack.push("python")
    print(f"字符串栈大小:{str_stack.size()}")
    print(f"栈顶元素:{str_stack.peek()}")
    print(f"出栈:{str_stack.pop()}")
    print(f"出栈后大小:{str_stack.size()}")

运行结果:

整数栈大小:3
栈顶元素:3
出栈:3
出栈后大小:2
------------------------------
字符串栈大小:3
栈顶元素:python
出栈:python
出栈后大小:2

说明:

  • Stack[int] 表示存放整数的栈
  • Stack[str] 表示存放字符串的栈
  • 同一个类可以处理不同类型的元素
  • 类型检查器可以验证类型是否正确

4.3 多个类型参数 #

一个泛型类可以接受多个类型参数。

# 导入 TypeVar 和 Generic
from typing import TypeVar, Generic

# 定义多个类型变量
K = TypeVar('K')  # Key 类型
V = TypeVar('V')  # Value 类型

# 定义泛型键值对类
# Generic[K, V] 表示这个类接受两个类型参数:K 和 V
class Pair(Generic[K, V]):
    """键值对:键类型为 K,值类型为 V"""

    def __init__(self, key: K, value: V):
        # 初始化键值对
        self.key = key
        self.value = value

    def get_key(self) -> K:
        # 获取键,返回类型为 K
        return self.key

    def get_value(self) -> V:
        # 获取值,返回类型为 V
        return self.value

    def __str__(self) -> str:
        # 字符串表示
        return f"Pair(key={self.key}, value={self.value})"

# 主程序入口
if __name__ == "__main__":
    # 创建整数键、字符串值的键值对
    # Pair[int, str] 表示键是 int,值是 str
    pair1: Pair[int, str] = Pair(1, "one")
    print(f"键值对1:{pair1}")
    print(f"键类型:{type(pair1.get_key()).__name__}")
    print(f"值类型:{type(pair1.get_value()).__name__}")

    print("-" * 30)

    # 创建字符串键、浮点数值的键值对
    # Pair[str, float] 表示键是 str,值是 float
    pair2: Pair[str, float] = Pair("pi", 3.14)
    print(f"键值对2:{pair2}")
    print(f"键类型:{type(pair2.get_key()).__name__}")
    print(f"值类型:{type(pair2.get_value()).__name__}")

    print("-" * 30)

    # 创建字符串键、列表值的键值对
    # Pair[str, list] 表示键是 str,值是 list
    pair3: Pair[str, list] = Pair("numbers", [1, 2, 3])
    print(f"键值对3:{pair3}")
    print(f"键类型:{type(pair3.get_key()).__name__}")
    print(f"值类型:{type(pair3.get_value()).__name__}")

运行结果:

键值对1:Pair(key=1, value=one)
键类型:int
值类型:str
------------------------------
键值对2:Pair(key=pi, value=3.14)
键类型:str
值类型:float
------------------------------
键值对3:Pair(key=numbers, value=[1, 2, 3])
键类型:str
值类型:list

说明:

  • Generic[K, V] 表示这个类接受两个类型参数
  • Pair[int, str] 表示键是 int,值是 str
  • Pair[str, float] 表示键是 str,值是 float
  • 可以灵活组合不同的类型

5. 实际应用示例 #

5.1 示例1:API 响应包装器 #

在实际项目中,API 响应通常有统一的结构。使用泛型可以让响应包装器处理不同类型的数据。

# 导入 TypeVar、Generic 和 Optional
from typing import TypeVar, Generic, Optional

# 定义类型变量 T
T = TypeVar('T')

# 定义 API 响应包装器类
class ApiResponse(Generic[T]):
    """API 响应包装器:可以包装任何类型的数据"""

    def __init__(
        self, 
        success: bool, 
        data: Optional[T] = None, 
        error: Optional[str] = None
    ):
        # 初始化响应
        # success: 是否成功
        # data: 响应数据(类型为 T)
        # error: 错误信息(如果有)
        self.success = success
        self.data = data
        self.error = error

    @classmethod
    def success_response(cls, data: T) -> 'ApiResponse[T]':
        # 创建成功响应
        # 返回类型是 ApiResponse[T]
        return cls(True, data=data)

    @classmethod
    def error_response(cls, error: str) -> 'ApiResponse[T]':
        # 创建错误响应
        return cls(False, error=error)

    def __str__(self) -> str:
        # 字符串表示
        if self.success:
            return f"ApiResponse(success=True, data={self.data})"
        else:
            return f"ApiResponse(success=False, error={self.error})"

# 主程序入口
if __name__ == "__main__":
    # 创建用户响应(数据是字典)
    user_response: ApiResponse[dict] = ApiResponse.success_response(
        {"id": 1, "name": "张三", "email": "zhangsan@example.com"}
    )
    print(f"用户响应:{user_response}")

    # 创建商品列表响应(数据是列表)
    product_response: ApiResponse[list] = ApiResponse.success_response(
        [{"id": 1, "name": "苹果"}, {"id": 2, "name": "香蕉"}]
    )
    print(f"商品响应:{product_response}")

    # 创建错误响应
    error_response: ApiResponse[dict] = ApiResponse.error_response(
        "用户不存在"
    )
    print(f"错误响应:{error_response}")

运行结果:

用户响应:ApiResponse(success=True, data={'id': 1, 'name': '张三', 'email': 'zhangsan@example.com'})
商品响应:ApiResponse(success=True, data=[{'id': 1, 'name': '苹果'}, {'id': 2, 'name': '香蕉'}])
错误响应:ApiResponse(success=False, error=用户不存在)

说明:

  • ApiResponse[dict] 表示响应数据是字典类型
  • ApiResponse[list] 表示响应数据是列表类型
  • 同一个类可以处理不同类型的数据
  • 类型检查器可以验证类型是否正确

5.2 示例2:带约束的泛型类 #

有时候,你希望泛型类只能接受特定类型的参数。

# 导入 TypeVar、Generic 和 List
from typing import TypeVar, Generic, List

# 定义只能接受数字类型的类型变量
# Number 只能是 int、float 或 complex
Number = TypeVar('Number', int, float, complex)

# 定义数字统计类
class Statistics(Generic[Number]):
    """数字统计:只能处理数字类型的数据"""

    def __init__(self, data: List[Number]):
        # 初始化数据列表
        self.data = data

    def sum(self) -> Number:
        # 计算总和,返回类型为 Number
        return sum(self.data)

    def mean(self) -> float:
        # 计算平均值,返回类型为 float
        return sum(self.data) / len(self.data) if self.data else 0.0

    def max(self) -> Number:
        # 返回最大值
        return max(self.data) if self.data else 0

    def min(self) -> Number:
        # 返回最小值
        return min(self.data) if self.data else 0

# 主程序入口
if __name__ == "__main__":
    # 使用整数列表(允许)
    int_stats: Statistics[int] = Statistics([1, 2, 3, 4, 5])
    print(f"整数统计:")
    print(f"  总和:{int_stats.sum()}")
    print(f"  平均值:{int_stats.mean():.2f}")
    print(f"  最大值:{int_stats.max()}")
    print(f"  最小值:{int_stats.min()}")

    print("-" * 30)

    # 使用浮点数列表(允许)
    float_stats: Statistics[float] = Statistics([1.5, 2.5, 3.5, 4.5])
    print(f"浮点数统计:")
    print(f"  总和:{float_stats.sum()}")
    print(f"  平均值:{float_stats.mean():.2f}")
    print(f"  最大值:{float_stats.max()}")
    print(f"  最小值:{float_stats.min()}")

    # 使用字符串列表(不允许,类型检查器会报错)
    # str_stats: Statistics[str] = Statistics(["a", "b", "c"])  # 类型错误!

运行结果:

整数统计:
  总和:15
  平均值:3.00
  最大值:5
  最小值:1
------------------------------
浮点数统计:
  总和:12.0
  平均值:3.00
  最大值:4.5
  最小值:1.5

说明:

  • Statistics[int] 表示处理整数列表
  • Statistics[float] 表示处理浮点数列表
  • Statistics[str] 不允许(类型检查器会报错)
  • 类型约束确保类只处理特定类型的数据

5.3 示例3:简单的链表 #

# 导入 TypeVar、Generic 和 Optional
from typing import TypeVar, Generic, Optional

# 定义类型变量 T
T = TypeVar('T')

# 定义链表节点类
class Node(Generic[T]):
    """链表节点:存放类型为 T 的数据"""

    def __init__(self, data: T):
        # 初始化节点
        # data: 节点数据(类型为 T)
        # next: 指向下一个节点的指针
        self.data = data
        self.next: Optional[Node[T]] = None

    def __str__(self) -> str:
        # 字符串表示
        return f"Node(data={self.data})"

# 定义链表类
class LinkedList(Generic[T]):
    """链表:可以存放任何类型的元素"""

    def __init__(self):
        # 初始化空链表
        self.head: Optional[Node[T]] = None

    def append(self, data: T) -> None:
        # 在链表末尾添加元素
        new_node = Node(data)
        if self.head is None:
            # 如果链表为空,新节点成为头节点
            self.head = new_node
        else:
            # 找到最后一个节点
            current = self.head
            while current.next is not None:
                current = current.next
            # 将新节点添加到末尾
            current.next = new_node

    def display(self) -> None:
        # 显示链表内容
        if self.head is None:
            print("链表为空")
            return

        # 遍历链表并打印
        current = self.head
        items = []
        while current is not None:
            items.append(str(current.data))
            current = current.next
        print(" -> ".join(items))

# 主程序入口
if __name__ == "__main__":
    # 创建整数链表
    int_list: LinkedList[int] = LinkedList()
    int_list.append(1)
    int_list.append(2)
    int_list.append(3)
    print("整数链表:")
    int_list.display()

    print("-" * 30)

    # 创建字符串链表
    str_list: LinkedList[str] = LinkedList()
    str_list.append("a")
    str_list.append("b")
    str_list.append("c")
    print("字符串链表:")
    str_list.display()

运行结果:

整数链表:
1 -> 2 -> 3
------------------------------
字符串链表:
a -> b -> c

说明:

  • LinkedList[int] 表示存放整数的链表
  • LinkedList[str] 表示存放字符串的链表
  • 同一个类可以处理不同类型的元素
  • 类型检查器可以验证类型是否正确

6. 常见问题 #

6.1 什么时候使用泛型? #

使用泛型的场景:

  1. 容器类:如栈、队列、链表等,需要存放不同类型的数据
  2. 工具函数:如获取列表第一个元素、合并两个列表等
  3. API 响应:统一响应格式,但数据类型不同
  4. 数据处理:如排序、过滤等,可以处理不同类型的数据

不使用泛型的场景:

  1. 简单函数:只处理一种特定类型,不需要泛型
  2. 业务逻辑:特定业务场景,类型固定

6.2 TypeVar 和 Generic 的区别? #

  • TypeVar:定义类型变量,用于函数和类的类型参数
  • Generic:创建泛型类,让类可以接受类型参数

简单理解:

  • TypeVar 是"占位符",表示"某种类型"
  • Generic 是"模板",让类可以处理不同类型

6.3 类型检查器会检查泛型吗? #

是的,类型检查器(如 mypy、pyright)会检查泛型类型。

from typing import TypeVar, List

T = TypeVar('T')

def get_first(lst: List[T]) -> T:
    return lst[0]

# 类型检查器会验证类型
numbers: List[int] = [1, 2, 3]
result: int = get_first(numbers)  # 正确

strings: List[str] = ["a", "b"]
result2: int = get_first(strings)  # 错误:result2 应该是 str,不是 int

6.4 泛型会影响运行时性能吗? #

不会。泛型只是类型提示,在运行时会被忽略,不会影响性能。

# 运行时,这两段代码是等价的
def func1(lst: List[int]) -> int:
    return lst[0]

def func2(lst: List[T]) -> T:
    return lst[0]

7. 总结 #

7.1 核心概念回顾 #

  • TypeVar:定义类型变量,表示"某种类型"

    • 可以是任意类型:T = TypeVar('T')
    • 可以添加约束:Number = TypeVar('Number', int, float)
  • Generic:创建泛型类,让类可以接受类型参数

    • 单个类型参数:class Box(Generic[T])
    • 多个类型参数:class Pair(Generic[K, V])

7.2 使用泛型的好处 #

  1. 类型安全:在编码时就能发现类型错误
  2. 代码复用:同一逻辑可以处理不同类型的数据
  3. 代码清晰:明确表达"这个函数/类可以处理任何类型"
  4. IDE 支持:更好的自动完成和类型检查
← 上一节 Flask 下一节 heapq →

访问验证

请输入访问令牌

Token不正确,请重新输入