2010-05-14 16 views
14

मैं जितना संभव हो सके डेटाबेस कनेक्शन को रोकना चाहता हूं, क्योंकि यह कोड एक गहन उपयोग सर्वर पर चलाएगा और यहां लोगों ने मुझे पहले से ही बताया है कि डेटाबेस कनेक्शन हमेशा जितनी जल्दी हो सके बंद होना चाहिए।पायथन में, यह सुनिश्चित करने के लिए कि कोड कनेक्शन छोड़ने से पहले डेटाबेस कनेक्शन हमेशा बंद हो जाएगा?

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 से पहले चिंता करने की है, तो मेरे पास है बिना जोड़ सकते हैं ...

लेकिन यह कैसे मैं हमेशा सही तरीके से बंद कर सकता डीबी कि 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 

मुझे नहीं लगता कि वहाँ एक अपवाद के लिए ExitingCodeBlock के लिए इसी तरह कुछ भी है , यद्यपि मैं जानता हूँ कि वहाँ else कोशिश, लेकिन मुझे आशा है कि अजगर पहले से ही एक समान सुविधा है ...

या हो सकता है किसी ने मुझे एक प्रतिमान चाल कर सकते हैं सुझाव और मुझे बताओ कि इस भयंकर है और अत्यधिक की सलाह मुझे ऐसा कभी नहीं करने के लिए उस। हो सकता है कि यह चिंता करने के लिए कुछ है और MySQLdb इसे संभालने दें, या है ना?

उत्तर

20

परंपरागत दृष्टिकोण 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) 

अजगर 2.6 (और 2 के बाद से।5 from __future__ import with_statement के साथ), एक विकल्प है (हालांकि try/finally अभी भी पूरी तरह से अच्छी तरह से काम करता है!): with कथन। (ऊपर whatever वापस जाने के लिए, अगर आप चाहते हैं)

with somecontext as whatever: 
    # the work goes here 

एक संदर्भ एक __enter__ विधि, प्रविष्टि पर निष्पादित है और एक __exit__ विधि, बाहर निकलने पर मार डाला। लालित्य के बावजूद, चूंकि कोई मौजूदा संदर्भ नहीं है जो आपके इच्छित तरीके से काम करता है, काम को एक बनाने के लिए आवश्यक है (हालांकि contextlib के साथ 2.6 में कम किया गया) शायद यह सुझाव देना चाहिए कि पुरानी कोशिश/अंततः सर्वोत्तम है।

आप 2.6 है और contextlib की कोशिश करना चाहते हैं, तो यह एक तरह से आप यह कर सकता है ट्राई/अंत में "छिपाने" है ...:

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_ 
:

import contextlib 

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

के रूप में प्रयोग की जाने वाली

यदि आप इन कई उपयोगों में से प्रत्येक के लिए कोशिश/आखिरकार ओवर और ओवर को दोहराने से बचने के लिए, कई बार इसका उपयोग करने जा रहे हैं, तो यह इसके लायक हो सकता है।

+0

ओह ठीक नहीं है, आपके और माइकल ने आपके उत्तरों को संपादित करने के बाद हमारे पास 2 बहुत समान और पूर्ण ... को चुनना मुश्किल है। – cregox

6

यदि MySQLdb इसका समर्थन करता है, तो आप "with" कथन का उपयोग कर सकते हैं। उस कारण के साथ "साथ" कथन मौजूद है। हालांकि, यह आवश्यक है कि ऑब्जेक्ट उस काम के लिए __enter__ और __exit__ को परिभाषित करे।

बयान के साथ का एक उदाहरण के रूप में ... पढ़ने/फ़ाइलें लिखने के लिए, आप हो सकता है:

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

यह इसका समर्थन नहीं करता है, तो आप हमेशा कोशिश का उपयोग कर सकते हैं ... अंत में के रूप में में:

try: 
    # Do some processing 
finally: 
    # Cleanup 

अंत में खंड कोई फर्क नहीं पड़ता निष्पादित किया जाता है कैसे कोशिश खत्म (यह सफल पूरा या अपवाद का प्रचार किया गया था लेकिन वह पकड़ा है, या एक अपवाद उत्पन्न हुआ था और प्रचार करने के लिए जारी रहेगा या नहीं)।

+0

मैंने हाल ही में कोशिश की कि pyodbc के साथ और यह काम नहीं किया है, लेकिन आप बेहतर भाग्य हो सकता है। –

+1

एक रैपर में उपयुक्त कॉल लिखना बहुत कठिन नहीं होना चाहिए, यदि आपका विशेष ऑब्जेक्ट पहले से इसका समर्थन नहीं करता है। उद्धृत पेप आपको दिखाता है कि फाइलों के लिए इसे कैसे किया जाए। –

+1

यह जानना बहुत दिलचस्प है, लेकिन मुझे नहीं लगता कि यह MySQLdb द्वारा समर्थित है। यह निश्चित रूप से पायथन 2.5 – cregox

4

मान लीजिए कि आप जिस डीबी ड्राइवर का उपयोग कर रहे हैं वह बॉक्स के बाहर with का समर्थन नहीं करता है, the closing method from contextlib आज़माएं।