首页 > 编程问答 >如何在Python 3.12+中正确使用泛型来提高代码质量?

如何在Python 3.12+中正确使用泛型来提高代码质量?

时间:2024-07-28 04:49:55浏览次数:7  
标签:python python-typing python-3.12

我正在尝试使用泛型来改进 FastAPI 应用程序中的类型注释。我有一个抽象存储库类,在其中使用泛型:

from abc import ABC, abstractmethod
from typing import List


class AbstractRepository[T](ABC):
    @abstractmethod
    async def add_one(self, data: dict) -> T:
        ...

    @abstractmethod
    async def delete(self, id: int) -> T:
        ...

    @abstractmethod
    async def update(self, id: int, data: dict) -> T:
        ...

    @abstractmethod
    async def get_one(self, id: int) -> T | None:
        ...

    @abstractmethod
    async def get_all(self) -> List[T]:
        ...

名为 SQLAlchemyRepository 的类继承自该抽象类。在此类中,我将泛型绑定到 Base ,这是所有 SQLAlchemy 模型继承自的类:

from typing import Type, List

from sqlalchemy import insert, delete, update, select

from src.database import Database
from src.models import Base
from src.repositories import AbstractRepository


class SQLAlchemyRepository(AbstractRepository[Base]):
    _model: Type[Base] = None

    def __init__(self):
        self.__database: Database = Database()

    async def add_one(self, data: dict) -> Base:
        async with self.__database.get_async_session() as session:
            stmt = insert(self._model).values(**data).returning(self._model)
            res = await session.execute(stmt)
            await session.commit()
            return res.scalar_one()

    async def delete(self, id: int) -> Base:
        async with self.__database.get_async_session() as session:
            stmt = delete(self._model).filter_by(id=id).returning(self._model)
            res = await session.execute(stmt)
            await session.commit()
            return res.scalar_one()

    async def update(self, id: int, data: dict) -> Base:
        async with self.__database.get_async_session() as session:
            stmt = update(self._model).values(**data).filter_by(id=id).returning(self._model)
            res = await session.execute(stmt)
            return res.scalar_one()

    async def get_one(self, id: int) -> Base | None:
        async with self.__database.get_async_session() as session:
            stmt = select(self._model).filter_by(id=id)
            res = await session.execute(stmt)
            res = res.scalar_one_or_none()
            return res

    async def get_all(self) -> List[Base]:
        async with self.__database.get_async_session() as session:
            stmt = select(self._model)
            res = await session.execute(stmt)
            res = res.scalars().all()
            return res

特定 SQLAlchemy 模型的存储库从此存储库继承:

from . import SQLAlchemyRepository
from src.models import Users


class UsersRepository(SQLAlchemyRepository):
    _model = Users

我是否正确使用泛型?我阅读了 Python 3.12 泛型的文档,但找不到任何使用泛型的真实企业示例。


你对于泛型的使用总体来说是 正确 的,并且展现了对泛型概念的良好理解。你成功地使用泛型创建了一个抽象的存储库类,并将其应用于具体的SQLAlchemyRepository和UsersRepository。

以下是一些额外的建议和改进:

1. 泛型类型约束:

  • 虽然你的代码可以正常工作,但你可以通过添加类型约束来进一步提高代码的类型安全性。例如,在 AbstractRepository 中, add_one , update 方法中的 data: dict 可以使用 TypedDict 来更精确地定义字典的结构,并与具体的模型属性对应。

```python from typing import TypedDict

class UserCreateData(TypedDict): name: str email: str

class AbstractRepository T : @abstractmethod async def add_one(self, data: UserCreateData) -> T: ... ```

2. 类型推断:

  • SQLAlchemyRepository 中,你不需要显式地将 _model 的类型注释为 Type[Base] ,因为类型推断可以自动识别它的类型。

```python class SQLAlchemyRepository(AbstractRepository[Base]): _model = None # 类型将被自动推断为 Type[Base]

```

3. 依赖注入:

  • 为了提高代码的可测试性和可维护性,可以考虑使用依赖注入来管理 Database 实例,而不是在 SQLAlchemyRepository 的构造函数中直接实例化它。

python class SQLAlchemyRepository(AbstractRepository[Base]): def __init__(self, database: Database): self.__database = database

4. 错误处理:

  • 在真实的企业级应用中,你需要考虑添加适当的错误处理机制。例如,当数据库操作失败时,你应该捕获异常并返回有意义的错误信息。

python async def get_one(self, id: int) -> Base | None: async with self.__database.get_async_session() as session: try: stmt = select(self._model).filter_by(id=id) res = await session.execute(stmt) return res.scalar_one_or_none() except SQLAlchemyError as e: # 处理数据库错误 raise HTTPException(status_code=500, detail=str(e))

总结:

你的代码已经很好地使用了泛型,并展现了对类型提示的良好理解。通过添加类型约束,使用类型推断,采用依赖注入和实现更完善的错误处理,你可以进一步提高代码的质量和可维护性,使其更接近真实的企业级应用标准。

标签:python,python-typing,python-3.12
From: 78802372

相关文章

  • python中的while循环不退出
    我试图完成第一年的python商业课程作业,但我的while循环无法退出,有人能帮忙吗?commisionTable=[{"admin_fee":100,"comm_rate":0.10},{"admin_fee":125,"comm_rate":0.12},{"admin_fee":150,"comm_rate":......
  • python---json文件写入
    ​ 使用到的知识点:os模块执行linux指令、json.dump()、withopenasf代码实现importsysimportosimportjson #向json文件file中添加内容data,其中data的类型为字典defwrite_json(file,data):    #如果文件存在,则删除    if(os.path.exists(fi......
  • python错题记录:布尔运算与逻辑值检测
    一前言环境:python3.10win10二布尔运算与逻辑值检测1案例案例1如上,在布尔运算时,有些时候代码只会运算前面的一部分,剩下的部分根本不会运算。以前在练习算法代码时,就利用这个规则来减少代码的工作量案例2如上,之前好长一段时间,上面的布尔运算总是让我感到困惑布尔运......
  • python---字典遍历
    1、三种常见的字典遍历实现defget_key_value(dics):  '''遍历所有键值对'''  forkey,valueindics.items():    print(f"{key}:{value}")defget_keys(dics):  '''遍历所有的键'''  forkeyindics......
  • python基本语法三天速成系列day1(看完这篇你就会)
    注释注释是代码非常重要的一部分,它的主要作用有:解释代码目的:注释可以说明代码段或函数的目的和功能,帮助其他开发者快速理解代码的意图。复杂逻辑说明:对于复杂的算法或业务逻辑,通过注释可以解释这些逻辑是如何工作的,降低后续维护的难度。提高可读性:良好的注释可以使代码结......
  • Python学习笔记46:游戏篇之外星人入侵(七)
    前言到目前为止,我们已经完成了游戏窗口的创建,飞船的加载,飞船的移动,发射子弹等功能。很高兴的说一声,基础的游戏功能已经完成一半了,再过几天我们就可以尝试驾驶飞船击毁外星人了。当然,计分,游戏次数,背景音乐,开始启动等按钮的功能需要我们慢慢添加,这些功能不影响游戏的使用,影......
  • Python学习笔记45:游戏篇之外星人入侵(六)
    前言飞船模块的功能基本已经完成。今天继续完成子弹模块的功能。子弹模块子弹和飞船模块,在游戏逻辑中有一种生成与被生成的表面关系,因为子弹在游戏中是由飞船发射的。但是在我们实际抽象的过程中,飞船与子弹并不是is的关系,甚至可以说不是has的关系。因此我们需要将两个对......
  • 三种语言实现二分(C++/Python/Java)
    题目给定一个按照升序排列的长度为......
  • python+flask计算机毕业设计农场营销管理系统(程序+开题+论文)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着现代农业的快速发展,农场管理日益复杂,尤其是营销环节,传统的销售模式已难以满足市场快速变化的需求。农场主面临着如何高效管理农资采购......
  • python+flask计算机毕业设计社区独居老人健康管理系统(程序+开题+论文)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着社会老龄化的加速,独居老人群体日益庞大,成为社会关注的焦点。这一群体在享受独立生活的同时,也面临着健康监测不及时、生活照料缺失、医......