在Python中,如何在离开代码块之前确保数据库连接始终关闭?

时间:2010-05-14 21:44:46

标签: python nested database-connection

我想尽可能防止数据库连接被打开,因为这段代码将在密集使用的服务器上运行,这里的人已经告诉我应该尽快关闭数据库连接。

def do_something_that_needs_database ():
    dbConnection = MySQLdb.connect(host=args['database_host'], user=args['database_user'], passwd=args['database_pass'], db=args['database_tabl'], cursorclass=MySQLdb.cursors.DictCursor)
    dbCursor = dbConnection.cursor()
    dbCursor.execute('SELECT COUNT(*) total FROM table')
    row = dbCursor.fetchone()
    if row['total'] == 0:
        print 'error: table have no records'
        dbCursor.execute('UPDATE table SET field="%s"', whatever_value)
        return None
    print 'table is ok'
    dbCursor.execute('UPDATE table SET field="%s"', another_value)

    # a lot more of workflow done here

    dbConnection.close()

    # even more stuff would come below

我相信当桌面上没有行时,会打开数据库连接,I'm still really not sure how it works

无论如何,也许这是糟糕的设计,因为我可以在execute的每个小块之后打开和关闭数据库连接。当然,我可以在close之前添加一个return ...

但是,我怎么能总是正确地关闭数据库而不必担心我是否有returnraisecontinue,或者其他什么?我正在考虑像代码块一样,类似于使用try,就像在下面的建议中一样,这显然不起作用:

def do_something_that_needs_database ():
    dbConnection = MySQLdb.connect(host=args['database_host'], user=args['database_user'], passwd=args['database_pass'], db=args['database_tabl'], cursorclass=MySQLdb.cursors.DictCursor)
    try:
        dbCursor = dbConnection.cursor()
        dbCursor.execute('SELECT COUNT(*) total FROM table')
        row = dbCursor.fetchone()
        if row['total'] == 0:
            print 'error: table have no records'
            dbCursor.execute('UPDATE table SET field="%s"', whatever_value)
            return None
        print 'table is ok'
        dbCursor.execute('UPDATE table SET field="%s"', another_value)
        # again, that same lot of line codes done here
    except ExitingCodeBlock:
        closeDb(dbConnection)
    # still, that "even more stuff" from before would come below

我认为没有类似于ExitingCodeBlock的异常,我知道有尝试 else,但我希望Python已经有类似的特征...

也许有人可以建议我采取范式,并告诉我这很糟糕,并且高度建议我永远不要这样做。也许这只是不用担心的问题,让MySQLdb处理它,或者是它?

4 个答案:

答案 0 :(得分:23)

传统方法是try / finally声明:

def do_something_that_needs_database ():
    dbConnection = MySQLdb.connect(host=args['database_host'], user=args['database_user'], passwd=args['database_pass'], db=args['database_tabl'], cursorclass=MySQLdb.cursors.DictCursor)
    try:
       # as much work as you want, including return, raising exceptions, _whatever_
    finally:
       closeDb(dbConnection)

自Python 2.6(以及带有from __future__ import with_statement的2.5)以来,有一种替代方法(尽管try / finally仍能正常运行!):with语句。

with somecontext as whatever:
   # the work goes here

上下文有__enter__方法,在进入时执行(如果需要,返回上面的whatever)和退出时执行的__exit__方法。尽管优雅,但由于没有现有的上下文按照你想要的方式工作,所以构建一个所需的工作(虽然用contextlib减少了2.6)应该可能表明好的旧尝试/最终是最好的。

如果你有2.6并想尝试contextlib,这是你可以用来“隐藏”try / finally的一种方式......:

import contextlib

@contextlib.contextmanager
def dbconnect(**kwds):
  dbConnection = MySQLdb.connect(**kwds)
  try:
    yield dbConnection
  finally:
    closeDb(dbConnection)

用作:

def do_something_that_needs_database ():
    with dbconnect(host=args['database_host'], user=args['database_user'], 
                   passwd=args['database_pass'], db=args['database_tabl'], 
                   cursorclass=MySQLdb.cursors.DictCursor) as dbConnection:
       # as much work as you want, including return, raising exceptions, _whatever_

如果你打算多次使用它,可能是值得的,只是为了避免重复尝试/最后反复进行这些用途。

答案 1 :(得分:6)

如果MySQLdb支持它,那么你可以使用“with”语句。出于这个原因,存在“with”语句。但是,它要求对象定义__enter__和__exit__以使其起作用。

作为with语句的一个例子......对于读/写文件,你可能有:

with open('filename','r') as file:
    for line in file:
        # processing....
# File automatically closed afterwards or if there was an exception thrown

如果它不支持它,那么你总是可以使用try ... finally,如:

try:
    # Do some processing
finally:
    # Cleanup

无论try是如何完成的,都会执行finally子句(无论它是成功完成,还是传播但是被捕获,或者抛出异常并继续传播)。

答案 2 :(得分:4)

假设您使用的数据库驱动程序不支持with开箱即用,请尝试the closing method from contextlib

答案 3 :(得分:3)

为什么不把它包装成try:finally:block?

http://docs.python.org/tutorial/errors.html#defining-clean-up-actions

这就是最终阻止的目标。