首页 > 编程问答 >如果查询不在构造函数中,为什么 Sqlalchemy 会清理查询结果?

如果查询不在构造函数中,为什么 Sqlalchemy 会清理查询结果?

时间:2024-07-26 14:16:26浏览次数:8  
标签:python python-3.x sqlalchemy

我创建了一个类 Result ,它接受 sqlalchemy 语句,存储该语句,执行该语句,并存储执行结果。

如果直接在 __init__() 中执行该语句,下面的脚本将产生预期的结果:

1  Test JE 1
2  Test JE 2

但是如果该语句在 set_result() 中执行,下面的脚本输出 No results. Using pdb 显示 _cleanup() set_result() 之后调用:

--Call--
> /usr/lib/python3/dist-packages/sqlalchemy/orm/state.py(420)_cleanup()

为什么 _cleanup() set_result() 之后被调用但不是在 __init__() ?

脚本之后

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import Column, String, Integer, select

Base = declarative_base()

class JournalEntry(Base):
    __tablename__ = 'JournalEntry'

    pid = Column(Integer, primary_key=True)
    desc = Column(String, nullable=False)

    def __repr__(self):
        return f"{self.pid}  {self.desc}"

class db:
    engine = create_engine('sqlite:///:memory:', echo=False)
    Base.metadata.create_all(engine)
    Session = sessionmaker(engine)

class Result:
    """Helper for printing query results."""
    def __init__(self, stmt):
        self.stmt = stmt
        with db.Session() as session:
            self.result = session.scalars(self.stmt).all()
        #self.result = self.set_result()

    def set_result(self):
        with db.Session() as session:
            self.result = session.scalars(self.stmt).all()
        breakpoint()
        # objects in self.result get cleaned

    def __repr__(self):
        try:
            resultstr = ""
            for obj in self.result:
                resultstr += f"{obj}\n"
            return resultstr
        except TypeError:
            return "No results."

if __name__ == '__main__':
    je1 = JournalEntry(desc="Test JE 1")
    je2 = JournalEntry(desc="Test JE 2")

    with db.Session.begin() as session:
        session.add_all([je1, je2])

    gj = Result(select(JournalEntry))
    print(gj)


SQLAlchemy 在 set_result() 后调用 _cleanup() 的原因在于你如何在两种情况下管理数据库会话的生命周期,以及 Python 中垃圾回收的工作原理。

让我们来分解一下:

  1. init 中的会话: 当你在 __init__ 方法中执行查询时,你正在 with db.Session() as session: 块内执行查询。这个块会创建一个数据库会话,并将其作为 session 关键点在于,这个 session 对象会在 with 块结束后超出作用域,这也会自动关闭会话并清理结果。 然而,由于 Result 对象仍然存在,并且其 self.result 属性持有对结果对象的引用,因此这些对象在被垃圾回收之前不会被清理。

  2. set_result 中的会话: 当你在 set_result 方法中执行查询时,你也在 with db.Session() as session: 块内执行查询。 然而,与 __init__ 不同的是, set_result 方法在 with 块结束后不会立即返回。 这意味着,当 set_result 返回时,数据库会话仍然处于打开状态,并且结果对象仍然绑定到该会话。

  3. 垃圾回收和弱引用: SQLAlchemy 使用弱引用来跟踪与会话关联的对象。当一个对象不再被任何强引用引用时,Python 的垃圾回收器可以自由地回收它。由于在 set_result 中,会话在方法返回后仍然处于打开状态,因此结果对象仍然具有一个来自会话的强引用。 set_result 返回并且没有其他对结果对象的引用时,它们就会成为垃圾回收的目标。 此时,SQLAlchemy 会介入并调用 _cleanup() 来断开结果对象与会话的连接,并释放任何资源。

简而言之:

  • __init__ 中,会话在结果被使用之前关闭,因此结果在 Result 对象的生命周期内保持有效。
  • set_result 中,会话在方法返回后保持打开状态。当 set_result 返回并且没有其他对结果对象的引用时,它们就会成为垃圾回收的目标,从而触发 SQLAlchemy 的 _cleanup() 机制。

如何解决:

为了解决这个问题,你可以:

  1. Result 对象的生命周期内保持会话打开: 这可以通过将 db.Session() 实例存储为 Result 类的一个属性来实现。但是,这需要你手动管理会话的生命周期,并确保在不再需要 Result 对象时关闭会话。

  2. set_result 返回之前显式地从会话中分离结果: 可以使用 session.expunge_all() 方法来实现。这将断开结果对象与会话的连接,并防止它们在 set_result 返回后被清理。

  3. 使用 yield_per stream_results : 对于大型结果集,你可以使用 SQLAlchemy 的 yield_per stream_results 功能来逐行或逐块迭代结果。这将允许你在处理结果时保持低内存占用,并避免 _cleanup() 问题。

希望这能解释清楚!

标签:python,python-3.x,sqlalchemy
From: 78790799

相关文章

  • 使用 Python 进行数据分析:入门指南
    使用Python进行数据分析:入门指南1.简介本指南将介绍如何使用Python进行数据分析,涵盖从数据加载到可视化分析的各个方面。2.必要的库NumPy:用于数值计算和数组操作。Pandas:用于数据处理和分析,提供DataFrame结构。Matplotlib:用于数据可视化,创建各种图表。Seab......
  • IT实战课堂计算机毕业设计源码精品基于Python的高校教育教材采购出入库进销存储信息管
    项目功能简介:《[含文档+PPT+源码等]精品基于Python的高校教育教材信息管理系统设计与实现》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功以及课程答疑与微信售后交流群、送查重系统不限次数免费查重等福利!软件开发环境及开发工具:开......
  • 为什么我的 Python 脚本失败并出现 TypeError?
    我正在编写一个Python脚本,该脚本应该计算数字列表的总和。但是,当我运行代码时遇到TypeError这是一个最小的例子:numbers=[1,2,3,'4']total=sum(numbers)print(total)Theerrormessageis:TypeError:unsupportedoperandtype(s)for+:'int'and'str......
  • 如何通过socks代理传递所有Python的流量?
    有如何通过http代理传递所有Python的流量?但是,它不处理sock代理。我想使用sock代理,我们可以通过ssh隧道轻松获得它。ssh-D5005user@server你可以使用socks库,让你的Python代码通过SOCKS代理传递所有流量。这个库可以让你在套接字级别上指定代......
  • 如何在streamlit python中流式传输由LLM生成的输出
    代码:fromlangchain_community.vectorstoresimportFAISSfromlangchain_community.embeddingsimportHuggingFaceEmbeddingsfromlangchainimportPromptTemplatefromlangchain_community.llmsimportLlamaCppfromlangchain.chainsimportRetrievalQAimports......
  • python mysql操作
    pipinstallmysql-connector-pythonimportmysql.connector#配置数据库连接参数config={'user':'your_username','password':'your_password','host':'your_host','database'......
  • Python 中的面向对象编程
    一.介绍在本文中,我们将使用Python中的类和对象来探索基本的OOP概念。面向对象编程(OOP)是一种强大的方法,可帮助开发人员组织代码,使其易于理解、重用和维护。Python是一种灵活的语言,可以很好地支持OOP概念。1.类和对象类是创建对象的蓝图。它定义了该类的对象将......
  • 学习资源系列之《Python深度学习基于PyTorch》
     前言近期应部分读者朋友的强烈邀请,希望推荐一本python深度学习实操的书籍。呐,今天为大家推荐小编偶然发现的这一本珍藏好书:《Python深度学习基于PyTorch》,文末附电子版获取方式《Python深度学习基于PyTorch》BriefIntroduction前言面对众多的深......
  • 如何使用Python实现语音转文字/字幕
    文章目录......
  • Python 教程(三):字符串特性大全
    目录专栏列表前言1.字符串基础2.字符串方法字符串查询字符串修改字符串切片3.字符串格式化旧式格式化(`%`操作符)`str.format()`方法f-string(Python3.6+)4.字符串编码5.Unicode和ASCII6.正则表达式7.字符串比较8.字符串连接9.字符串不可变性10.字符串的内......