2012-04-05 8 views
7

これはまだ動作するはずですが、株価テーブルはないと言われています - コンテキストマネージャのどこかで接続が失われていると思われますか?Pythonなぜ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 statementcontextlibについて再読み込みドキュメントしたい場合があります。

あなたのコードのもう一つの問題は、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) 

しかし、これはこれを行う最もクリーンな方法ではないと私は考えています。私が見る限り、3つの別々のオブジェクトを作成する理由はありません。クエリごとに同じオブジェクトを使用できます。 conn.commitの呼び出しは実際には必要ではないと思われます。コンテキストマネージャーとしてデータベース接続を使用するとトランザクションが自動的にコミットされます。例外が発生した場合はロールバックします(sqlite3 module documentationを参照)。

EDIT:これはまだまだ機能する、きれいなバージョンです。私は本当にカーソルを閉じることが実際に何をするのか分からない - それはおそらく必要ではない(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 tableinsert 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

これは正常機能([ジェネレータ]とは対照的に(http://docs.python.org/reference/simple_stmts.html#yield)に 'contextmanager'デコレータを使用しても意味がありません)。コンテキストマネージャの全体的なポイントは、withステートメントでそれを使用することです。withステートメントで 'doquery2'の戻り値を試してみると、' TypeError'が得られます。 – James

+0

私はコードがどこで壊れているかを指摘しています。 doquery関数はAFAICTを冗長化したものです。あなたが問題の範囲をあまり狭く見ていればそれは問題ありませんが、私の答えが最も単純な方法で説明していることを知っている限り、なぜポスターが働いていないのかがうまくいかないのです。 –

関連する問題