在对象销毁时清理内部pysqlite连接
我有一个对象,它内部有一个数据库连接,这个连接在整个对象的生命周期内都是活跃的。在程序结束时,这个连接需要被提交并关闭。到目前为止,我一直使用一个明确的 close
方法来关闭连接,但这有点麻烦,特别是当调用代码中可能发生异常的时候。
我在考虑使用 __del__
方法来关闭连接,但在网上阅读了一些资料后,我有些担心。这种用法合理吗?我能确保在 __del__
中内部资源会被正确释放吗?
这个讨论提出了类似的问题,但没有找到令人满意的答案。我不想要一个明确的 close
方法,而使用 with
也不是一个选项,因为我的对象并不是简单的打开-使用-关闭,而是作为另一个更大对象的成员,在图形用户界面中运行时使用。
C++ 有非常有效的析构函数,可以安全地释放资源,所以我想 Python 也应该有类似的约定。但出于某种原因,似乎并不是这样,社区中的许多人反对使用 __del__
。那么,还有什么替代方案呢?
2 个回答
了解一下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()
有些人把这个称为“依赖注入”和“控制反转”。还有些人称之为策略模式。“必须关闭的对象”就是一种策略,它被插入到某个更大的对象中。这个组合通常是在图形用户界面应用的顶层创建的,因为通常在这里会获取像数据库这样的资源。
你可以创建一个连接模块,因为模块在整个应用程序中保持同一个对象,并且可以用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甚至“更好”,因为它有垃圾回收器。