SQLAIchemy 异步DBManager封装-01入门理解

前言

SQLAlchemy 是一个强大的 Python SQL 工具包和对象关系映射(ORM)系统,是业内比较流行的ORM,设计非常优雅。随着其2.0版本的发布,SQLAlchemy 引入了原生的异步支持,这极大地增强了其在处理高并发和异步I/O场景下的能力。通过结合像greenlet、gevent这样的协程库,SQLAlchemy 使得异步数据库操作成为可能,从而提高了应用程序的性能和响应速度。

这里我将基于SQLAlchemy的异步支持,封装一些常用的增删改查(CRUD)操作到 https://github.com/HuiDBK/py-tools 中,以便在项目开发中更加便捷地使用。

Github: https://github.com/sqlalchemy/sqlalchemy

2.0文档:https://docs.sqlalchemy.org/en/20/index.html

简单使用

封装前,先简单介绍下如何使用 SQLAIchemy。

具体细节可以参考官网文档:https://docs.sqlalchemy.org/en/20/orm/quickstart.html

安装依赖

pip install sqlalchemy[asyncio]==2.0.20
pip install aiomysql==0.2.0

这里安装了 sqlalchemy 2.0版本,以及 aiomysql 异步数据库驱动,进行演示。

创建异步数据库引擎

from sqlalchemy.ext.asyncio import create_async_engine  # db_uri = "{protocol}://{user}:{password}@{host}:{port}/{db}"db_engine = create_async_engine("mysql+aiomysql://root:123456@127.0.0.1:3306/demo")

声明数据库表映射模型

from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_columnclass BaseOrmTable(DeclarativeBase):"""SQLAlchemy Base ORM Model"""__abstract__ = Trueid: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, comment="主键ID")class UserTable(BaseOrmTable):"""用户表"""__tablename__ = "user"username: Mapped[str] = mapped_column(String(30), default="", comment="用户昵称")password: Mapped[str] = mapped_column(String(30), default="", comment="用户密码")phone: Mapped[str] = mapped_column(String(11), default="", comment="手机号")email: Mapped[str] = mapped_column(String(30), default="", comment="邮箱")avatar: Mapped[str] = mapped_column(String(100), default="", comment="头像")

简单db操作


from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column# db_uri = "{protocol}://{user}:{password}@{host}:{port}/{db}"db_engine = create_async_engine("mysql+aiomysql://root:123456@127.0.0.1:3306/hui-demo")Session = async_sessionmaker(db_engine)async def create_tables():# 根据映射创建库表async with db_engine.begin() as conn:await conn.run_sync(BaseOrmTable.metadata.create_all)async def main():await create_tables()async with Session.begin() as session:# 添加用户new_user = UserTable(username='hui', email='huidbk@163.com')session.add(new_user)await session.flush()   # 刷新table 对象属性,获取新增的idprint(new_user.id)print("add user", new_user.__dict__)# 获取用户user = await session.get(UserTable, new_user.id)print("get user", user.__dict__)# 更新用户user.email = 'hui@163.com'await session.merge(user)print("updated user", user.__dict__)# 删除用户await session.delete(user)if __name__ == '__main__':# 运行主函数asyncio.run(main())

常用DB操作封装

SQLAlchemyManager

class SQLAlchemyManager(metaclass=SingletonMetaCls):DB_URL_TEMPLATE = "{protocol}://{user}:{password}@{host}:{port}/{db}"def __init__(self,host: str = "localhost",port: int = 3306,user: str = "",password: str = "",db_name: str = "",pool_size: int = 30,pool_pre_ping: bool = True,pool_recycle: int = 600,log: Union[logging.Logger] = None,):self.host = hostself.port = portself.user = userself.password = passwordself.db_name = db_nameself.pool_size = pool_sizeself.pool_pre_ping = pool_pre_pingself.pool_recycle = pool_recycleself.log = log or loggerself.db_engine: AsyncEngine = Noneself.async_session_maker: async_sessionmaker = Nonedef get_db_url(self, protocol: str = "mysql+aiomysql"):db_url = self.DB_URL_TEMPLATE.format(protocol=protocol, user=self.user, password=self.password, host=self.host, port=self.port, db=self.db_name)return db_urldef init_db_engine(self, protocol: str):"""初始化db引擎Args:protocol: 驱动协议类型Returns:self.db_engine"""db_url = self.get_db_url(protocol=protocol)self.log.info(f"init_db_engine => {db_url}")self.db_engine = create_async_engine(url=db_url, pool_size=self.pool_size, pool_pre_ping=self.pool_pre_ping, pool_recycle=self.pool_recycle)self.async_session_maker = async_sessionmaker(bind=self.db_engine, expire_on_commit=False)return self.db_enginedef init_mysql_engine(self, protocol: str = "mysql+aiomysql"):"""初始化mysql引擎Args:protocol: 驱动协议类型Returns:self.db_engine"""return self.init_db_engine(protocol=protocol) 

SQLAlchemyManager 主要封装一些数据库账户配置信息、连接池信息。

pool_size(连接池大小): 指定连接池中允许保持的最大连接数。当应用程序需要访问数据库时,连接池会维护一定数量的数据库连接,以便快速地响应请求。通常情况下,pool_size 的值应该根据应用程序的并发访问量和数据库的性能来进行调整。

pool_pre_ping(预检查连接): 指定是否在数据库连接被使用前对连接进行预检查。预检查可以确保连接处于活动状态,并且可以自动重新连接到数据库服务器,以防止连接由于长时间空闲而失效。启用预检查可以提高应用程序对数据库的可靠性和稳定性。

pool_recycle(连接回收时间): 指定数据库连接在被重新使用之前的最大空闲时间。当连接空闲时间超过 pool_recycle 设置的值时,连接将被关闭并重新创建,以防止连接长时间处于空闲状态而导致的连接问题。pool_recycle 的值通常设置为一个较小的时间间隔,以确保连接能够及时地得到回收和重建,从而提高连接的健壮性和性能。

init_db_engine 方法则是初始化数据库引擎,内部根据数据库配置信息

  • 构造异步的数据库引擎 db_engine
  • 维护一个 async_session_maker 数据库会话工厂

BaseORMTable 映射库表封装

from datetime import datetime
from sqlalchemy import func
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_columnclass BaseOrmTable(AsyncAttrs, DeclarativeBase):"""SQLAlchemy Base ORM Model"""__abstract__ = Trueid: Mapped[int] = mapped_column(primary_key=True, comment="主键ID")def __repr__(self):return str(self.to_dict())def to_dict(self, alias_dict: dict = None, exclude_none=True) -> dict:"""数据库模型转成字典Args:alias_dict: 字段别名字典 eg: {"id": "user_id"}, 把id名称替换成 user_idexclude_none: 默认排查None值Returns: dict"""alias_dict = alias_dict or {}if exclude_none:return {alias_dict.get(c.name, c.name): getattr(self, c.name)for c in self.__table__.columns if getattr(self, c.name) is not None}else:return {alias_dict.get(c.name, c.name): getattr(self, c.name, None)for c in self.__table__.columns}class TimestampColumns(AsyncAttrs, DeclarativeBase):"""时间戳相关列"""__abstract__ = Truecreated_at: Mapped[datetime] = mapped_column(default=datetime.now, comment="创建时间")updated_at: Mapped[datetime] = mapped_column(default=datetime.now, onupdate=datetime.now, comment="更新时间")deleted_at: Mapped[datetime] = mapped_column(nullable=True, comment="删除时间")class BaseOrmTableWithTS(BaseOrmTable, TimestampColumns):__abstract__ = True

创建一些基础的 ORM 类,以便后续的映射类可以继承并且共享一些公有属性和方法。

  1. BaseOrmTable 类:

    1. 定义了一个基础的 ORM 模型类,继承了 AsyncAttrsDeclarativeBase。这样做使得 BaseOrmTable 类具有了异步属性访问的能力,为异步编程提供便利,特别是在异步环境中访问具有延迟加载或者异步加载特性的属性。
    2. 提供了一个 to_dict 方法,用于将数据库模型转换为字典。它支持通过参数 alias_dict 指定字段别名,并且可以选择是否排除值为 None 的属性。
  2. TimestampColumns 类:

    1. 定义了一个包含时间戳相关列的抽象基类。这些列通常在很多数据库表中都会有,用于记录数据的创建时间、更新时间和删除时间。
    2. 这些列被设置为默认值,比如 created_atupdated_at 默认使用 datetime.now 函数来自动记录当前时间,deleted_at 则允许为空,用于标记数据的删除时间(可用作于逻辑删除)
  3. BaseOrmTableWithTS 类:

    1. 继承了 BaseOrmTableTimestampColumns,实际上是一个组合类,集成了基础的 ORM 功能和时间戳相关的列。
    2. 这个类进一步封装了 BaseOrmTableTimestampColumns,使得后续的映射类只需要继承这个类,就能够拥有基础的 ORM 功能和时间戳相关的列。

通过这种封装,你可以在后续的数据库映射类中更加专注于业务逻辑的实现,而不需要重复编写基础的 ORM 功能和时间戳相关的列,提高了代码的重用性和可维护性。

DBManager 数据库通用操作封装

前置封装说明

from typing import Any, List, Type, TypeVar, Union
from py_tools.connections.db.mysql import BaseOrmTable
from py_tools.meta_cls import SingletonMetaCls# 泛指 BaseOrmTable 所有子类实例对象类型   
T_BaseOrmTable = TypeVar("T_BaseOrmTable", bound=BaseOrmTable)
T_Hints = TypeVar("T_Hints")  # 用于修复被装饰的函数参数提示,让IDE有类型提示def with_session(method) -> T_Hints:"""兼容事务会话Args:method: orm 的 crudNotes:方法中没有带事务连接则,则构造Returns:"""@functools.wraps(method)async def wrapper(db_manager, *args, **kwargs):session = kwargs.get("session") or Noneif session:return await method(db_manager, *args, **kwargs)else:async with db_manager.transaction() as session:kwargs["session"] = sessionreturn await method(db_manager, *args, **kwargs)return wrapper

这里我提供了一个 with_session 装饰器,用于在需要数据库会话(事务)的数据库操作方法中自动开启事务,由于 sqlaichemy 官方推荐每个数据库操作都手动开启事务会话(自动提交),装饰器的设计没有时则构造,有则共享,这样不但可以减少冗余 async with db_manager.transaction() as session 的代码,也可以兼容多个操作共享同一个 session 有问题时进行事务回滚。

由于给方法加了通用的装饰器导致一些版本的IDE无法识别方法真实的签名,使用时会出现不知道方法的入参是什么,对于开发者来说是极其不方便的。

使用 typing 的 TypeVar 自定义类型来构造一个通用的泛型来当作函数返回的类型,进而修复。

from typing import TypeVar
T_Hints = TypeVar("T_Hints")  # 用于修复被装饰的函数参数提示,让IDE有类型提示def with_session(method) -> T_Hints:...

这里PyCharm 2023.2.4 版本升级到 2024.1 就有提示了,IDE修复了,可以不用 T_Hints 了。

一些旧版本构造 sqlaichemy 的库表对象时也会出现不知道类对象属性入参提示,升级到最新版本都解决了。

from contextlib import asynccontextmanagerclass DBManager(metaclass=SingletonMetaCls):DB_CLIENT: SQLAlchemyManager = Noneorm_table: Type[BaseOrmTable] = None@classmethoddef init_db_client(cls, db_client: SQLAlchemyManager):cls.DB_CLIENT = db_clientreturn cls.DB_CLIENT@classmethod@asynccontextmanagerasync def transaction(cls):"""事务上下文管理器"""async with cls.DB_CLIENT.async_session_maker.begin() as session:yield session@classmethod@asynccontextmanagerasync def connection(cls) -> AsyncIterator[AsyncConnection]:"""数据库引擎连接上下文管理器"""async with cls.DB_CLIENT.db_engine.begin() as conn:yield conn
  • init_db_client 方法用于初始化数据库客户端(引擎)。
  • transaction 则是简单的通过 contextlib 中 asynccontextmanager 封装一个异步的上下文管理器方便简洁的开启一个数据库会话(事务)进行数据库相关操作。
  • connection 数据库引擎连接上下文管理器。
  • orm_table 是具体继承 DBManager 的子类进行指定的,用于操作具体的库表(orm_table)。
  • DBManager 通过 SingletonMetaCls 元类实现单例模式。具体单例模式可以了解 https://juejin.cn/post/7272006755265380367 这篇文章有详细的介绍。

DB添加操作封装

    
class DBManager(metaclass=SingletonMetaCls):DB_CLIENT: SQLAlchemyManager = Noneorm_table: Type[BaseOrmTable] = None@with_sessionasync def bulk_add(self,table_objs: List[Union[T_BaseOrmTable, dict]],*,orm_table: Type[BaseOrmTable] = None,flush: bool = False,session: AsyncSession = None) -> List[T_BaseOrmTable]:"""批量插入Args:table_objs: orm映射类实例列表eg.[UserTable(username="hui", age=18), ...] or [{"username": "hui", "age": 18}, ...]orm_table: orm表映射类flush: 刷新对象状态,默认不刷新session: 数据库会话对象,如果为 None,则通过装饰器在方法内部开启新的事务Returns:成功插入的对象列表"""orm_table = orm_table or self.orm_tableif all(isinstance(table_obj, dict) for table_obj in table_objs):# 字典列表转成orm映射类实例列表处理table_objs = [orm_table(**table_obj) for table_obj in table_objs]session.add_all(table_objs)if flush:await session.flush(table_objs)return table_objs@with_sessionasync def add(self,table_obj: [T_BaseOrmTable, dict],*,orm_table: Type[BaseOrmTable] = None,session: AsyncSession = None) -> int:"""插入一条数据Args:table_obj: orm映射类实例对象, eg. UserTable(username="hui", age=18) or {"username": "hui", "age": 18}orm_table: orm表映射类session: 数据库会话对象,如果为 None,则通过装饰器在方法内部开启新的事务Returns: 新增的idtable_obj.id"""orm_table = orm_table or self.orm_tableif isinstance(table_obj, dict):table_obj = orm_table(**table_obj)session.add(table_obj)await session.flush(objects=[table_obj])  # 刷新对象状态,获取新增的idreturn table_obj.id

这里就是用 session.add 与 add_all 方法封装了数据库添加、批量添加的操作,封装的点主要在于除了 orm_table 实例对象入参还支持字典入参,内部还是转换成库表映射类实例来操作,最后通过 session.flush 方法,单个添加返回新增的主键id,批量添加则是返回实例对象列表。

设计的方法中有一个 * 号是参数的分隔符,它的作用是将其前面的参数声明为位置参数,而将 * 后面的参数声明为关键字参数,* 号后面的参数入参只能使用关键字形式的入参,我在很多的开源库中都看到了这样的设计,可以把一些函数语义连贯、常用必传的参数设置为位置参数,其他的则是关键字参数。这样可以明确参数的作用、提高函数的可读性、防止参数错误等。

具体看下使用案例:

import asynciofrom sqlalchemy import String
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_columnfrom py_tools.connections.db.mysql import BaseOrmTableWithTS, BaseOrmTable, DBManager, SQLAlchemyManagerclass UserTable(BaseOrmTableWithTS):"""用户表"""__tablename__ = "user"username: Mapped[str] = mapped_column(String(30), default="", comment="用户昵称")password: Mapped[str] = mapped_column(String(30), default="", comment="用户密码")phone: Mapped[str] = mapped_column(String(11), default="", comment="手机号")email: Mapped[str] = mapped_column(String(30), default="", comment="邮箱")avatar: Mapped[str] = mapped_column(String(100), default="", comment="头像")async def create_tables():# 根据映射创建库表(异步)# async with db_engine.begin() as conn:#    await conn.run_sync(BaseOrmTable.metadata.create_all)async with DBManager.connection() as conn:await conn.run_sync(BaseOrmTable.metadata.create_all)async def init_orm_manager():db_client = SQLAlchemyManager(host="127.0.0.1",port=3306,user="root",password="123456",db_name="hui-demo",)db_client.init_mysql_engine()DBManager().init_db_client(db_client)async def manager_crud():user = {"username": "hui", "email": "huidbk.163.com"}user_id = await DBManager().add(table_obj=user, orm_table=UserTable)print("user_id", user_id)users = [{"username": "zack", "email": "zack.163.com"},{"username": "wang", "email": "wang.163.com"}]add_users = await DBManager().bulk_add(table_objs=users, orm_table=UserTable)add_user_ids = [user.id for user in add_users]print("add_user_ids", add_user_ids)async def main():await create_tables()# await normal_crud()await init_orm_manager()await manager_crud()if __name__ == '__main__':# 运行主函数asyncio.run(main())

在程序启动时初始化好DBManager 的 DB_CLIENT 就可以直接使用封装的方法,主要就是 DB_CLIENT 作为类属性,后面DBManager 实例与子类实例对象都可以共享这个数据库引擎。但我这里还是不推荐上面的写法,DBManager 是一些通用的DB操作,而具体一些业务操作还是单独封装一些DB业务Manager类来进行会比较好,更利于扩展维护与复用。


class UserManager(DBManager):orm_table = UserTableasync def get_name_by_email(self, email):username = await self.query_one(cols=["username"], conds=[self.orm_table.email == email], flat=True)return usernameasync def manager_crud():# demo 2 (推荐)user = UserTable(username="hui-test01", email="hui-test01.163.com")user_id = await UserManager().add(table_obj=user)print("user_id", user_id)users = [UserTable(username="hui-test02", email="hui-test02.163.com"),UserTable(username="hui-test03", email="hui-test03.163.com"),]add_users = await UserManager().bulk_add(table_objs=users)add_user_ids = [user.id for user in add_users]print("add_user_ids", add_user_ids)username = await UserManager().get_name_by_email(email="huidbk.163.com")print("username", username)>>> out
user_id 4
add_user_ids [5, 6]
username hui

这里 UserManager 单独封装的 get_name_by_email 的方法就是业务中常用查询操作通过邮件获取用户名称,这里就是举一个简单的例子,具体DB业务具体封装而不是全部写在逻辑层,这样别人要用的时候就不用重新组织条件参数、上下文,而是简单传递业务参数进行复用获取数据。

UserManager 调用 add、bulk_add 等方法时也不用像 DBManager 指定 orm_table 参数,使用起来更简洁。具体是因为 UserManager 类指定了 类属性 orm_table = UserTable,再封装时有一句 orm_table = orm_table or self.orm_table 意思就是优先选择入参的orm_table,没有则是 self.orm_table (具体实例对象的orm_table)。这样写也体现出 封装、继承的灵活性。

这里也引出了另一个封装方法 query_one 查询单条数据。由于介绍了一些Demo如果把所有的封装方法混合到一起篇幅就太长,故而我准备分成三篇进行分别介绍,这样也更好阅读。

  1. SQLAIchemy 异步DBManager封装-01入门理解
  2. SQLAIchemy 异步DBManager封装-02熟悉掌握
  3. SQLAIchemy 异步DBManager封装-03得心应手

Github源代码

源代码已上传到了Github,里面也有具体的使用Demo,欢迎大家一起体验、贡献。

HuiDBK/py-tools: 打造 Python 开发常用的工具,让Coding变得更简单 (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/1599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows 的常用命令(不分大小写)

Net user (查看当前系统所有的账户) net user yourname password /add 添加新用户 net localgroup administrators yourname /add 添加管理员权限 net user yourname /delete 删除用户 net user 命令 [colorred]说明:以下命令仅限持管理员…

opencv人脸打马赛克

import cv2def FaceFind(imgPath: str) -> list:image cv2.imread(imgPath)gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 返回人脸坐标列表faces face_cascade.detectMultiScale(gray, scal…

数据结构11:二叉树的链式结构

文章目录 快速创建链式二叉树二叉树的遍历前序、中序、后序层序 二叉树的基本操作二叉树的节点个数二叉树叶节点的个数二叉树第k层结点个数二叉树查找值为x的结点 二叉树基础oj练习单值二叉树检查两颗树是否相同对称二叉树二叉树的前序遍历另一颗树的子树 二叉树的创建和销毁二…

谷雨时节,雨水渐多湿气旺盛,吃什么养生?听听张婉如医生怎么说

谷雨春光晓,山川黛色青。 叶间鸣戴胜,泽水长浮萍。 4月19日21时59分迎来谷雨,雨生百谷,这是谷雨节气的意思,它是春季的最后一个节气,这个时节早晚温差大,空气湿气重,如何养生呢&am…

java在线问卷调查系统的设计与实现(springboot+mysql源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的在线问卷调查系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于java的在线问卷调查…

电脑便签怎么固定位置 能固定在桌面的电脑便签

在繁忙的工作中,电脑便签是我离不开的小助手。每当灵感闪现,或是需要记录待办事项时,我总会打开便签,快速地记录下来。它就像我电脑屏幕上的一块“记事板”,随时提醒我未完成的工作和即将到来的任务。 但有一段时间&a…

i管家空间不足提醒怎么关闭

i管家的空间不足提醒是为了提醒用户手机存储空间不足,可能会影响手机的正常运行。目前,这个提醒功能是无法直接关闭的。如果您希望减少这类提醒的出现,可以尝试以下几种方法: 清理手机存储:检查手机中是否有不需要的文…

restful请求风格的增删改查-----查询and添加

一、restful风格的介绍 restful也称之为REST ( Representational State Transfer ),可以将它理解为一种软件架构风格或设计风格,而不是一个标准。简单来说,restful风格就是把请求参数变成请求路径的一种风格。例如,传统的URL请求…

Darknet,看过很多篇,这个最清晰了

Darknet深度学习框架:YOLO背后的强大支持 Darknet,一个由Joseph Redmon开发的轻量级神经网络框架,以其在计算机视觉任务,特别是目标检测中的卓越表现而闻名。本文将详细介绍Darknet的基本概念、结构以及它在深度学习领域的应用。…

UE4_动画基础_根运动Root Motion

学习笔记,仅供参考! 在游戏动画中,角色的碰撞胶囊体(或其他形状)通常由控制器驱动通过场景。然后来自该胶囊体的数据用于驱动动画。例如,如果胶囊体在向前移动,系统就会知道在角色上播放一个跑步…

Kivy Pyinstaller Windows 打包

各种报错 ImportErrorWhenRunningHook: Failed to import module __PyInstaller_hooks_0_kivy required by hook for module 三天美好时光啥也没干,就研究这个了。 打包成功,运行应用程序exe闪退的。终于打包成功了。 这所有的原因都是因为我爱你。如果…

小型架构实验模拟

一 实验需求 二 实验环境 22 机器: 做nginx 反向代理 做静态资源服务器 装 nginx keepalived filebeat 44机器: 做22 机器的备胎 装nginx keepalived 99机器:做mysql的主 装mysqld 装node 装filebeat 77机器:做mysq…

谷歌Gemini 1.5 Pro国内怎么用?国内镜像来了

长期以来,许多人向我咨询是否存在一个稳定而高效的全球AI大模型测试平台,这个平台需要不仅真实可靠,而且能够提供稳定和快速的服务,不会频繁出现故障或响应缓慢的问题。然而,当我发现了AskManyAI时,我被其所…

蛋白质治病突变的计算方法(三)

3 用于识别致病突变的特征 文献中使用了几种特征来识别蛋白质中的致病突变。它们大致分为三类:(1)序列,(2)结构和(3)网络,以及它们的组合。图1说明了这三组中的一些重要属性。 图1 用于识别致病突变和热点的重要特征。 基于氨基酸序列的特性…

李宏毅2022机器学习/深度学习 个人笔记(3)

本系列用于推导、记录该系列视频中本人不熟悉、或认为有价值的知识点 本篇记录代码效果不佳时应该怎么做 如下图所示: 接下来探讨,当optimization不佳的时候,如何判断是遇到了鞍点还是遇到了局部最小值点?可以通过多元函数的泰勒…

【大语言模型+Lora微调】10条对话微调Qwen-7B-Chat并进行推理 (聊天助手)

代码:https://github.com/QwenLM/Qwen/tree/main 国内源安装说明:https://modelscope.cn/models/qwen/Qwen-7B-Chat/summary 通义千问:https://tongyi.aliyun.com/qianwen 一、环境搭建 下载源码 git clone https://github.com/QwenLM/Qwen…

是用computed获取vuex数据后,修改数据页面不响应的问题

问题描述: 代码里使用computed获取mapGetters的数据后,直接在页面使用,在methods中更新数据后,控制台打印数据已经更改,但是页面上的数据没有同步更改和响应。 分析: 1.computed是计算属性,所有…

【Linux 进程间通信】管道(三)

文章目录 1.管道的五种特征2.管道的四种情况 1.管道的五种特征 ①🍎匿名管道只能用于有血缘关系的进程之间进行通信(爷孙进程之间可以进行通信),常用于父子之间进行通信; ②🍎管道内部,自带进…

【数据结构】时间复杂度的例题

🎁个人主页:我们的五年 🔍系列专栏:数据结构 🌷追光的人,终会万丈光芒 前言: 这篇文章是关于时间复杂度的一些例题,关于时间复杂度和空间复杂度和算法的计算效率的基本知识点我放在…

Linux之C编程入门

目录 第1关:第一个C程序 任务描述 相关知识 编译C程序 编程要求 答案及其步骤: 第2关:Linux编译C程序 任务描述 相关知识 gcc编译器使用方法 编程要求 答案及其步骤: 第3关:Linux之静态库编写 任务描述 相关知识 生成…