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

cre*_*gox 18 python database-connection nested

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

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
Run Code Online (Sandbox Code Playgroud)

我相信当桌子上没有行时会打开数据库连接,所以我仍然不确定它是如何工作的.

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

但是,我怎么能总是正确关闭数据库,而不必担心,如果我有return,或raise,或continue,或任何中间?我正在考虑像代码块一样的东西,类似于使用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
Run Code Online (Sandbox Code Playgroud)

我不认为有什么类似ExitingCodeBlock的例外,我知道有尝试 else,但我希望Python已经有类似的功能......

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

Ale*_*lli 25

传统方法是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)
Run Code Online (Sandbox Code Playgroud)

从Python 2.6(和2.5 with a from __future__ import with_statement)开始,有一个替代方案(尽管try/ finally仍然可以很好地工作!):with声明.

with somecontext as whatever:
   # the work goes here
Run Code Online (Sandbox Code Playgroud)

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

如果您有2.6并想尝试contextlib,这是一种可以"隐藏"try/finally ...的方法:

import contextlib

@contextlib.contextmanager
def dbconnect(**kwds):
  dbConnection = MySQLdb.connect(**kwds)
  try:
    yield dbConnection
  finally:
    closeDb(dbConnection)
Run Code Online (Sandbox Code Playgroud)

用作:

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_
Run Code Online (Sandbox Code Playgroud)

如果你要使用这么多次,可能是值得的,只是为了避免重复尝试/最后反复使用这些中的每一个.


Mic*_*yan 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
Run Code Online (Sandbox Code Playgroud)

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

try:
    # Do some processing
finally:
    # Cleanup
Run Code Online (Sandbox Code Playgroud)

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