2012-04-05 23 views
7

यह अभी तक काम करने वाला है, अभी तक कोई स्टॉक टेबल नहीं कहता है - संदर्भ प्रबंधक के अंदर कहीं कनेक्शन खो गया है?पायथन एक sqlite3 कर्सर काम के लिए एक संदर्भ प्रबंधक क्यों नहीं लिख रहा है?

import sqlite3 
from contextlib import contextmanager 

@contextmanager 
def doquery(conn, q, params=()): 
    c = conn.cursor() 
    c.execute(q, params) 
    conn.commit() 
    yield c  
    c.close() 

with sqlite3.connect(':memory:') as db:  
    doquery(db,'''create table stocks 
    (date text, trans text, symbol text, 
    qty real, price real)''') 

    doquery(db,"""insert into stocks 
      values ('2006-01-05','BUY','RHAT',100,35.14)""") 

    with doquery(db, 'select * from stocks') as r: 
     for row in r: 
      print row 

उत्तर

15

समस्या जिस तरह से आप संदर्भ प्रबंधक का उपयोग कर रहे है। कॉलिंग doquery बस एक संदर्भ प्रबंधक ऑब्जेक्ट बनाता है - आपको इसे with कथन के भीतर उपयोग करने की आवश्यकता है, जो उचित रूप से __enter__ और __exit__ विधियों को कॉल करता है। उदाहरण के लिए, कोशिश निम्नलिखित:

from contextlib import contextmanager 

@contextmanager 
def enter_exit(text): 
    print('entering') 
    yield text 
    print('exiting') 

print(enter_exit('attempt 1')) 

with enter_exit('attempt 2') as t: 
    print(t) 

उत्पादन मैं मिलता है:

<contextlib._GeneratorContextManager object at 0xcf3e90> 
entering 
attempt 2 
exiting 

आप दस्तावेज़ के बारे में the with statement और contextlib फिर से पढ़ सकते हैं।

अपने कोड के साथ एक और समस्या यह है कि अगर c.execute या conn.commit एक अपवाद को जन्म देती है, c.close बुलाया नहीं किया जाएगा है - यह है कि यदि वास्तव में आवश्यक है मैं नहीं जानता, लेकिन शायद यह कारण है कि यदि आप कोई संदर्भ प्रबंधक का उपयोग करना चाहते है पहली जगह में एक समारोह के बजाय। निम्नलिखित परिवर्तनों को दोनों समस्याओं को ठीक करना चाहिए:

import sqlite3 
from contextlib import contextmanager 

@contextmanager 
def doquery(conn, q, params=()): 
    c = conn.cursor() 
    try: 
     c.execute(q, params) 
     conn.commit() 
     yield c 
    finally: 
     c.close() 

with sqlite3.connect(':memory:') as db: 
    with doquery(db,'''create table stocks 
       (date text, trans text, symbol text, 
       qty real, price real)'''): 
     pass 

    with doquery(db,"""insert into stocks 
       values ('2006-01-05','BUY','RHAT',100,35.14)"""): 
     pass 

    with doquery(db, 'select * from stocks') as r: 
     for row in r: 
      print(row) 

हालांकि, मुझे नहीं लगता कि यह करने का यह सबसे साफ तरीका है। जहां तक ​​मैं देख सकता हूं, तीन अलग cursor ऑब्जेक्ट्स बनाने का कोई कारण नहीं है - आप प्रत्येक क्वेरी के लिए उसी का उपयोग कर सकते हैं। मुझे नहीं लगता कि conn.commit पर कॉल वास्तव में आवश्यक है - डेटाबेस कनेक्शन का उपयोग कर संदर्भ प्रबंधक के रूप में स्वचालित रूप से लेनदेन कर देगा, या अपवाद उठाए जाने पर उन्हें वापस रोल करें (sqlite3 module documentation देखें)।

संपादित करें: यहां एक बहुत साफ संस्करण है, जो अभी भी काम करता है। मैं वास्तव में नहीं जानता कि कर्सर वास्तव में क्या बंद कर रहा है - शायद यह आवश्यक नहीं है (Cursor.close दस्तावेज भी प्रतीत नहीं होता है)।

import sqlite3 
from contextlib import closing 

with sqlite3.connect(':memory:') as db: 
    with closing(db.cursor()) as c: 
     c.execute('''create table stocks 
       (date text, trans text, symbol text, 
       qty real, price real)''') 
     c.execute("""insert into stocks 
       values ('2006-01-05','BUY','RHAT',100,35.14)""") 
     c.execute('select * from stocks') 
     for row in c: 
      print(row) 
0

ऐसा नहीं है कि yieldcreate table और insert into बयानों के साथ दखल दे रहा है लगता है।

निम्नलिखित मैं select के अलावा के लिए yield का उपयोग नहीं करते और यह ठीक काम करता है:

#!/usr/bin/python3 
import sqlite3 
from contextlib import contextmanager 

@contextmanager 
def doquery(conn, q, params=()): 
    c = conn.cursor() 
    c.execute(q, params) 
    conn.commit() 
    yield c 
    c.close() 

@contextmanager 
def doquery2(conn, q, params=()): 
    c = conn.cursor() 
    c.execute(q, params) 
    conn.commit() 
    c.close() 

with sqlite3.connect(':memory:') as db: 
    doquery2(db,'''create table stocks 
    (date text, trans text, symbol text, 
    qty real, price real)''') 

    doquery2(db,"""insert into stocks                             
      values ('2006-01-05','BUY','RHAT',100,35.14)""") 

    with doquery(db, 'select * from stocks') as r: 
     for row in r: 
      print(row[0]) 
+0

यह रूप में करने के लिए एक [जनरेटर] विरोध किया भावना एक सामान्य कार्य पर 'contextmanager' डेकोरेटर का उपयोग करने के (नहीं पड़ता है (http://docs.python.org/reference/simple_stmts.html#yield))। एक संदर्भ प्रबंधक का पूरा बिंदु इसे एक कथन में उपयोग करना है, और यदि आप एक कथन में 'doquery2' के वापसी मान का प्रयास करते हैं और उपयोग करते हैं, तो आपको 'TypeError' मिलता है। – James

+0

मैं केवल यह इंगित कर रहा हूं कि कोड कहां टूट जाता है। डॉक्यूरी फ़ंक्शन AFAICT अनावश्यक है। यदि आप प्रश्न के दायरे को कम संकीर्ण रूप से देखते हैं तो मैंने ठीक किया है, लेकिन जहां तक ​​मुझे पता है कि मेरा जवाब सबसे सरल तरीके से बताता है कि पोस्टर का काम क्यों नहीं करता है, काम नहीं करता है। –