在对象销毁时清理内部pysqlite连接

9 投票
2 回答
5871 浏览
提问于 2025-04-15 12:09

我有一个对象,它内部有一个数据库连接,这个连接在整个对象的生命周期内都是活跃的。在程序结束时,这个连接需要被提交并关闭。到目前为止,我一直使用一个明确的 close 方法来关闭连接,但这有点麻烦,特别是当调用代码中可能发生异常的时候。

我在考虑使用 __del__ 方法来关闭连接,但在网上阅读了一些资料后,我有些担心。这种用法合理吗?我能确保在 __del__ 中内部资源会被正确释放吗?

这个讨论提出了类似的问题,但没有找到令人满意的答案。我不想要一个明确的 close 方法,而使用 with 也不是一个选项,因为我的对象并不是简单的打开-使用-关闭,而是作为另一个更大对象的成员,在图形用户界面中运行时使用。

C++ 有非常有效的析构函数,可以安全地释放资源,所以我想 Python 也应该有类似的约定。但出于某种原因,似乎并不是这样,社区中的许多人反对使用 __del__。那么,还有什么替代方案呢?

2 个回答

8

了解一下with语句。你在描述它的使用场景。

你需要把你的连接放在一个“上下文管理器”类里,这个类要处理__enter____exit__这两个方法,这些方法是with语句用的。

想了解更多信息,可以查看PEP 343


编辑

“我的对象不是简单的打开-使用-关闭,而是作为另一个更大对象的一个成员存在。”

class AnObjectWhichMustBeClosed( object ):
    def __enter__( self ):
        # acquire
    def __exit__( self, type, value, traceback ):
        # release
    def open( self, dbConnectionInfo ):
        # open the connection, updating the state for __exit__ to handle.

class ALargerObject( object ):
    def __init__( self ):
        pass
    def injectTheObjectThatMustBeClosed( self, anObject ):
        self.useThis = anObject

class MyGuiApp( self ):
    def run( self ):
        # build GUI objects
        large = ALargeObject()
        with AnObjectWhichMustBeClosed() as x:
            large.injectTheObjectThatMustBeClosed( x )
            mainLoop()

有些人把这个称为“依赖注入”和“控制反转”。还有些人称之为策略模式。“必须关闭的对象”就是一种策略,它被插入到某个更大的对象中。这个组合通常是在图形用户界面应用的顶层创建的,因为通常在这里会获取像数据库这样的资源。

6

你可以创建一个连接模块,因为模块在整个应用程序中保持同一个对象,并且可以用atexit模块注册一个函数来关闭它。

# db.py:
import sqlite3
import atexit

con = None

def get_connection():
    global con
    if not con:
        con = sqlite3.connect('somedb.sqlite')
    atexit.register(close_connection, con)
    return con

def close_connection(some_con):
    some_con.commit()
    some_con.close()

# your_program.py
import db
con = db.get_connection()
cur = con.cursor()
cur.execute("SELECT ...")

这个建议是基于这样一个假设:在你的应用程序中,连接看起来像是一个单一的实例(也叫单例),而模块全局变量很好地提供了这个功能。

如果不是这种情况,那你可以使用析构函数。

不过,析构函数和垃圾回收器以及循环引用不太搭配(你必须在析构函数被调用之前自己去除循环引用),如果你需要多个连接,那就可以使用析构函数。只要不保留循环引用,否则你就得自己去打破它们。

另外,你说的关于C++的内容是错的。如果你在C++中使用析构函数,它们会在定义对象的代码块结束时被调用(就像Python的with),或者当你使用delete关键字时(这会释放用new创建的对象)。在这之外,你必须使用一个明确的close(),而不是析构函数。所以这和Python是一样的——Python甚至“更好”,因为它有垃圾回收器。

撰写回答