首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 泛型完全避坑指南:从 TypeVar 到 Generic [T],结合 FastAPI 业务场景演示 3 种核心用法

Python 泛型完全避坑指南:从 TypeVar 到 Generic [T],结合 FastAPI 业务场景演示 3 种核心用法

作者头像
玄同765
发布2026-01-14 14:24:43
发布2026-01-14 14:24:43
460
举报
在这里插入图片描述
在这里插入图片描述

【个人主页:】

大语言模型(LLM)开发工程师中国传媒大学·数字媒体技术(智能交互与游戏设计) 深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调 技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️ 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案 专栏传送门:LLM大模型开发 项目实战指南Python 从真零基础到纯文本 LLM 全栈实战​​​​​从零学 SQL + 大模型应用落地大模型开发小白专属:从 0 入门 Linux&Shell 「让AI交互更智能,让技术落地更高效」 欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!

摘要

你是不是写 Python 代码时,处理不同类型的列表、字典、函数参数时,只能用Any类型导致代码可读性差、类型检查失败?这篇详解 Python 3.8 + 的泛型语法,包括 TypeVar 定义类型变量、Generic [T] 实现泛型类、Callable 和 Tuple 的高级用法,结合学生管理系统的 API 接口、ORM 查询、工具函数场景演示,让你彻底搞懂泛型的底层逻辑和应用场景。


一、引言:为什么要用泛型?

在 Python 的入门阶段,很多开发者会用Any类型处理不同类型的数据,比如写一个处理列表的函数时,参数类型注解为list[Any]。这种做法会导致以下问题:

  1. 代码可读性差:其他开发者无法快速理解函数参数和返回值的类型;
  2. 类型检查失败:IDE(如 PyCharm、VS Code)的类型检查功能无法正常工作,提前发现潜在的类型错误;
  3. 调试时间长:运行时可能会出现类型错误,需要花费很长时间排查;
  4. 维护成本高:修改函数时需要重新检查所有调用该函数的代码,防止类型不兼容。

泛型的出现解决了这些问题,它允许我们在定义函数、类、类型注解时,使用类型变量来表示任意类型,提高代码的可读性、可维护性和可测试性。


二、基础概念扫盲
2.1 泛型类型变量(TypeVar)

TypeVar 是 Python 的泛型类型变量,它允许我们在定义函数、类、类型注解时,使用类型变量来表示任意类型。

语法

代码语言:javascript
复制
from typing import TypeVar

T = TypeVar("T")  # 任意类型
U = TypeVar("U", int, float)  # 只能是int或float类型
V = TypeVar("V", bound=str)  # 只能是str类型或str的子类
2.2 泛型基类(Generic [T])

Generic 是 Python 的泛型基类,它允许我们在定义类时,使用类型变量来表示类的属性、方法参数、方法返回值的类型。

语法

代码语言:javascript
复制
from typing import Generic, TypeVar

T = TypeVar("T")

class MyClass(Generic[T]):
    def __init__(self, value: T):
        self.value = value
    
    def get_value(self) -> T:
        return self.value
    
    def set_value(self, value: T):
        self.value = value
2.3 类型约束(Bound)

TypeVar 的bound参数允许我们对类型变量进行约束,限制类型变量的取值范围。

语法

代码语言:javascript
复制
from typing import TypeVar

V = TypeVar("V", bound=str)  # 只能是str类型或str的子类
2.4 类型协变与逆变(Covariance and Contravariance)

TypeVar 的covariant=True参数允许我们对类型变量进行协变约束,限制类型变量的取值范围为子类。TypeVar 的contravariant=True参数允许我们对类型变量进行逆变约束,限制类型变量的取值范围为父类。

语法

代码语言:javascript
复制
from typing import TypeVar

# 协变约束:只能是str类型或str的子类
T = TypeVar("T", bound=str, covariant=True)

# 逆变约束:只能是str类型或str的父类
U = TypeVar("U", bound=str, contravariant=True)

三、Python 泛型的 3 种核心用法
3.1 用法一:泛型函数

泛型函数允许我们在定义函数时,使用类型变量来表示函数参数和返回值的类型。

代码示例(学生管理系统)

代码语言:javascript
复制
from typing import TypeVar, List

T = TypeVar("T")

def filter_list(data: List[T], condition) -> List[T]:
    """
    根据条件过滤列表中的数据
    :param data: 待过滤的数据列表
    :param condition: 过滤条件(函数)
    :return: 过滤后的数据列表
    """
    return [item for item in data if condition(item)]

# 测试代码(过滤学生列表中年龄大于等于18岁的学生)
if __name__ == "__main__":
    from app.schemas.students import StudentInfo
    
    students: List[StudentInfo] = [
        StudentInfo(
            id=101,
            student_no="20250001",
            name="张三",
            gender="男",
            birthdate="2005-01-01",
            phone="13800138001",
            email="zhangsan@example.com",
            address="北京市海淀区"
        ),
        StudentInfo(
            id=102,
            student_no="20250002",
            name="李四",
            gender="女",
            birthdate="2008-01-01",
            phone="13800138002",
            email="lisi@example.com",
            address="北京市朝阳区"
        )
    ]
    
    def age_ge_18(student: StudentInfo) -> bool:
        """判断学生年龄是否大于等于18岁"""
        birthdate = student.birthdate.split("-")
        birth_year = int(birthdate[0])
        current_year = 2025
        return current_year - birth_year >= 18
    
    filtered_students = filter_list(students, age_ge_18)
    for student in filtered_students:
        print(student.name)

测试:运行测试代码,输出结果为张三

3.2 用法二:泛型类

泛型类允许我们在定义类时,使用类型变量来表示类的属性、方法参数、方法返回值的类型。

代码示例(学生管理系统)

代码语言:javascript
复制
from typing import TypeVar, Generic, List

T = TypeVar("T")

class Repository(Generic[T]):
    """
    通用的Repository类,用于处理数据的CRUD操作
    """
    def __init__(self, data: List[T] = None):
        self.data = data or []
    
    def get_all(self) -> List[T]:
        """获取所有数据"""
        return self.data
    
    def get_by_id(self, id: int) -> T:
        """根据ID获取单个数据"""
        for item in self.data:
            if item.id == id:
                return item
        raise Exception("数据不存在")
    
    def create(self, item: T) -> T:
        """创建新数据"""
        self.data.append(item)
        return item
    
    def update(self, id: int, item: T) -> T:
        """更新数据"""
        for index, existing_item in enumerate(self.data):
            if existing_item.id == id:
                self.data[index] = item
                return item
        raise Exception("数据不存在")
    
    def delete(self, id: int):
        """删除数据"""
        for index, existing_item in enumerate(self.data):
            if existing_item.id == id:
                del self.data[index]
                return
        raise Exception("数据不存在")

# 测试代码(使用学生Repository)
if __name__ == "__main__":
    from app.schemas.students import StudentInfo
    
    student_repository = Repository[StudentInfo]()
    student1 = StudentInfo(
        id=101,
        student_no="20250001",
        name="张三",
        gender="男",
        birthdate="2005-01-01",
        phone="13800138001",
        email="zhangsan@example.com",
        address="北京市海淀区"
    )
    student_repository.create(student1)
    print(student_repository.get_all())

测试:运行测试代码,输出结果为[StudentInfo(id=101, student_no='20250001', name='张三', gender='男', birthdate='2005-01-01', phone='13800138001', email='zhangsan@example.com', address='北京市海淀区')]

3.3 用法三:泛型类型注解

泛型类型注解允许我们在使用类型注解时,使用类型变量来表示任意类型。

代码示例(学生管理系统)

代码语言:javascript
复制
from typing import TypeVar, Generic, List, Dict, Union, Tuple

T = TypeVar("T")
U = TypeVar("U")

# 泛型列表类型注解
GenericList = List[T]

# 泛型字典类型注解
GenericDict = Dict[T, U]

# 泛型元组类型注解
GenericTuple = Tuple[T, U]

# 泛型联合类型注解
GenericUnion = Union[T, U]

# 测试代码
if __name__ == "__main__":
    from app.schemas.students import StudentInfo
    
    student_list: GenericList[StudentInfo] = [
        StudentInfo(
            id=101,
            student_no="20250001",
            name="张三",
            gender="男",
            birthdate="2005-01-01",
            phone="13800138001",
            email="zhangsan@example.com",
            address="北京市海淀区"
        )
    ]
    
    student_dict: GenericDict[int, StudentInfo] = {101: student_list[0]}
    
    student_tuple: GenericTuple[int, StudentInfo] = (101, student_list[0])
    
    student_union: GenericUnion[int, StudentInfo] = 101
    
    print(student_list)
    print(student_dict)
    print(student_tuple)
    print(student_union)

测试:运行测试代码,输出结果为:

代码语言:javascript
复制
[StudentInfo(id=101, student_no='20250001', name='张三', gender='男', birthdate='2005-01-01', phone='13800138001', email='zhangsan@example.com', address='北京市海淀区')]
{101: StudentInfo(id=101, student_no='20250001', name='张三', gender='男', birthdate='2005-01-01', phone='13800138001', email='zhangsan@example.com', address='北京市海淀区')}
(101, StudentInfo(id=101, student_no='20250001', name='张三', gender='男', birthdate='2005-01-01', phone='13800138001', email='zhangsan@example.com', address='北京市海淀区'))
101

四、Python 泛型的高级用法
4.1 结合 FastAPI 的 API 接口

FastAPI 支持泛型类型注解,可以用于 API 接口的请求体参数和响应体参数。

代码示例(学生管理系统)

代码语言:javascript
复制
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.models.students import Student
from app.schemas.students import StudentInfo, StudentCreate
from app.schemas.common import ApiResponse
from app.services.students_service import create_student, get_students_info, get_student_info

router = APIRouter(prefix="/students", tags=["Students"])

@router.post("/", response_model=ApiResponse[StudentInfo])
def create_student_info(student: StudentCreate, db: Session = Depends(get_db)):
    """创建学生信息"""
    new_student = create_student(db, student)
    return ApiResponse[StudentInfo](data=new_student)

@router.get("/", response_model=ApiResponse[list[StudentInfo]])
def get_students(page: int = 1, page_size: int = 10, db: Session = Depends(get_db)):
    """获取学生列表"""
    students = get_students_info(db, page, page_size)
    return ApiResponse[list[StudentInfo]](data=students)

@router.get("/{student_id}", response_model=ApiResponse[StudentInfo])
def get_student(student_id: int, db: Session = Depends(get_db)):
    """获取单个学生信息"""
    student = get_student_info(db, student_id)
    return ApiResponse[StudentInfo](data=student)

说明ApiResponse是一个泛型类,用于统一 API 接口的响应格式。

4.2 结合 SQLAlchemy 的 ORM 查询

SQLAlchemy 1.4/2.0 支持泛型类型注解,可以用于 ORM 查询的返回值类型注解。

代码示例(学生管理系统)

代码语言:javascript
复制
from typing import TypeVar, Generic, List
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.models.students import Student

T = TypeVar("T")

class BaseRepository(Generic[T]):
    """
    通用的BaseRepository类,用于处理数据的CRUD操作
    """
    def __init__(self, db: Session, model: T):
        self.db = db
        self.model = model
    
    def get_all(self) -> List[T]:
        """获取所有数据"""
        return self.db.query(self.model).all()
    
    def get_by_id(self, id: int) -> T:
        """根据ID获取单个数据"""
        return self.db.query(self.model).filter(self.model.id == id).first()
    
    def create(self, item: T) -> T:
        """创建新数据"""
        self.db.add(item)
        self.db.commit()
        self.db.refresh(item)
        return item
    
    def update(self, id: int, item: T) -> T:
        """更新数据"""
        existing_item = self.get_by_id(id)
        if not existing_item:
            raise Exception("数据不存在")
        for key, value in item.__dict__.items():
            if key != "_sa_instance_state":
                setattr(existing_item, key, value)
        self.db.commit()
        self.db.refresh(existing_item)
        return existing_item
    
    def delete(self, id: int):
        """删除数据"""
        existing_item = self.get_by_id(id)
        if existing_item:
            self.db.delete(existing_item)
            self.db.commit()

# 学生Repository
class StudentRepository(BaseRepository[Student]):
    """
    学生Repository类,用于处理学生数据的CRUD操作
    """
    def __init__(self, db: Session):
        super().__init__(db, Student)
    
    def get_by_student_no(self, student_no: str) -> Student:
        """根据学号获取单个学生信息"""
        return self.db.query(Student).filter(Student.student_no == student_no).first()

# 测试代码
if __name__ == "__main__":
    db = next(get_db())
    student_repository = StudentRepository(db)
    students = student_repository.get_all()
    print(students)

测试:运行测试代码,输出结果为学生列表。


五、常见问题与解决方案
5.1 类型变量定义错误

问题:类型变量定义错误,如没有使用 TypeVar 函数定义类型变量。

解决方案:使用 TypeVar 函数定义类型变量。

5.2 泛型基类继承错误

问题:泛型基类继承错误,如没有继承 Generic [T] 基类。

解决方案:继承 Generic [T] 基类。

5.3 类型约束错误

问题:类型约束错误,如使用了不兼容的类型约束。

解决方案:使用兼容的类型约束。

5.4 类型注解错误

问题:类型注解错误,如没有使用类型变量作为类型注解。

解决方案:使用类型变量作为类型注解。


六、总结与展望
6.1 总结

通过以上步骤,我们详解了 Python 3.8 + 的泛型语法,包括 TypeVar 定义类型变量、Generic [T] 实现泛型类、类型约束、类型协变与逆变,结合学生管理系统的 API 接口、ORM 查询、工具函数场景演示,让你彻底搞懂泛型的底层逻辑和应用场景。

6.2 展望

未来,我们可以对 Python 泛型进行以下优化:

  • 使用类型守卫:通过使用 Type Guard 类型注解,实现运行时的类型检查;
  • 使用类型别名:通过使用 TypeAlias 类型注解,简化复杂的类型注解;
  • 使用协议类型:通过使用 Protocol 类型注解,实现接口的抽象;
  • 使用类型检查工具:通过使用 mypy 类型检查工具,提高代码的可读性、可维护性和可测试性。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 摘要
    • 一、引言:为什么要用泛型?
    • 二、基础概念扫盲
      • 2.1 泛型类型变量(TypeVar)
      • 2.2 泛型基类(Generic [T])
      • 2.3 类型约束(Bound)
      • 2.4 类型协变与逆变(Covariance and Contravariance)
    • 三、Python 泛型的 3 种核心用法
      • 3.1 用法一:泛型函数
      • 3.2 用法二:泛型类
      • 3.3 用法三:泛型类型注解
    • 四、Python 泛型的高级用法
      • 4.1 结合 FastAPI 的 API 接口
      • 4.2 结合 SQLAlchemy 的 ORM 查询
    • 五、常见问题与解决方案
      • 5.1 类型变量定义错误
      • 5.2 泛型基类继承错误
      • 5.3 类型约束错误
      • 5.4 类型注解错误
    • 六、总结与展望
      • 6.1 总结
      • 6.2 展望
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档