2010-05-14 17 views
14

Tôi muốn ngăn chặn kết nối cơ sở dữ liệu đang mở càng nhiều càng tốt, vì mã này sẽ chạy trên một máy chủ được sử dụng chuyên sâu và những người ở đây đã nói với tôi rằng kết nối cơ sở dữ liệu phải luôn luôn đóng càng sớm càng tốt.Trong Python, làm thế nào để đảm bảo kết nối cơ sở dữ liệu sẽ luôn đóng trước khi rời khỏi một khối mã?

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 

Tôi tin rằng để lại kết nối cơ sở dữ liệu mở khi không có hàng trên bàn, tho I'm still really not sure how it works.

Dù sao, có thể đó là thiết kế xấu theo nghĩa là tôi có thể mở và đóng kết nối DB sau mỗi khối nhỏ execute. Và chắc chắn, tôi chỉ có thể thêm một close ngay trước khi return trong trường hợp đó ...

Nhưng làm thế nào có thể tôi luôn luôn đóng đúng DB mà không cần phải lo lắng nếu tôi có mà return, hoặc một raise, hoặc continue, hoặc bất cứ điều gì ở giữa? Tôi đang nghĩ đến trong một cái gì đó giống như một khối mã, tương tự như sử dụng try, giống như trong những gợi ý sau đây, mà rõ ràng là không hoạt động:

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 

Tôi không nghĩ rằng có bất cứ điều gì tương tự như ExitingCodeBlock cho một ngoại lệ , tho tôi biết có là thửelse, nhưng tôi hy vọng Python đã có một tính năng tương tự ...

Hoặc có thể ai đó có thể gợi ý cho tôi một động thái mô hình và cho tôi biết đây là khủng khiếp và đánh giá cao tư vấn cho tôi để không bao giờ làm cái đó. Có lẽ đây chỉ là một cái gì đó để không lo lắng về và để cho MySQLdb xử lý nó, hoặc là nó?

Trả lời

20

Cách tiếp cận truyền thống là try/finally tuyên bố:

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) 

Kể từ Python 2.6 (và 2.5 với số from __future__ import with_statement), có một giải pháp thay thế (mặc dù try/finally vẫn hoạt động hoàn hảo!): Tuyên bố with.

with somecontext as whatever: 
    # the work goes here 

Một bối cảnh có một phương pháp __enter__, thực nhập cảnh (để trả lại whatever trên, nếu bạn muốn) và một __exit__ phương pháp, thực thi trên lối ra. Mặc dù sự sang trọng, vì không có bối cảnh hiện tại hoạt động theo cách bạn muốn, công việc cần thiết để xây dựng một (mặc dù giảm 2.6 với contextlib) có lẽ nên gợi ý rằng thử cũ/cuối cùng tốt nhất là tốt nhất.

Nếu bạn có 2.6 và muốn thử contextlib, đây là một cách để bạn có thể làm điều đó để "che giấu" try/cuối cùng ...:

import contextlib 

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

được sử dụng như:

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_ 

Nó có thể là giá trị nó nếu bạn sẽ sử dụng này nhiều, nhiều lần, chỉ để tránh lặp đi lặp lại cố gắng/cuối cùng hơn và hơn cho mỗi người sử dụng nhiều.

+0

ồ, sau khi bạn và Michael đã chỉnh sửa câu trả lời của bạn, chúng tôi có 2 câu trả lời rất giống nhau và đầy đủ ... khó chọn. – cregox

6

Nếu MySQLdb hỗ trợ, thì bạn có thể sử dụng câu lệnh "with". Câu lệnh "with" tồn tại chỉ vì lý do đó. Tuy nhiên, nó yêu cầu đối tượng xác định __enter__ và __exit__ để làm việc đó.

Như một ví dụ về với tuyên bố ... cho việc đọc/ghi tập tin, bạn có thể có:

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

Nếu nó không hỗ trợ nó, sau đó bạn luôn có thể sử dụng thử ... cuối cùng là trong:

try: 
    # Do some processing 
finally: 
    # Cleanup 

các khoản cuối cùng được thực hiện bất kể có bao kết thúc thử (cho dù đó hoàn thành thành công, hoặc một ngoại lệ được truyền bá nhưng bị bắt, hoặc một ngoại lệ được ném ra và sẽ tiếp tục tuyên truyền).

+0

Gần đây tôi đã thử rằng với pyodbc và nó không hoạt động, nhưng bạn có thể có may mắn hơn. –

+1

Nó không phải là quá khó khăn để tác giả các cuộc gọi thích hợp mặc dù trong một wrapper, nếu đối tượng cụ thể của bạn không hỗ trợ nó đã. Các pep trích dẫn thậm chí cho bạn thấy làm thế nào để làm điều đó cho các tập tin. –

+1

Điều đó rất thú vị để biết, nhưng tôi không nghĩ rằng nó được hỗ trợ bởi MySQLdb. Nó chắc chắn không phải bằng Python 2.5 – cregox

Các vấn đề liên quan