問題は、あなたがコンテキストマネージャを使用している方法です。 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)
しかし、これはこれを行う最もクリーンな方法ではないと私は考えています。私が見る限り、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)
これは正常機能([ジェネレータ]とは対照的に(http://docs.python.org/reference/simple_stmts.html#yield)に 'contextmanager'デコレータを使用しても意味がありません)。コンテキストマネージャの全体的なポイントは、withステートメントでそれを使用することです。withステートメントで 'doquery2'の戻り値を試してみると、' TypeError'が得られます。 – James
私はコードがどこで壊れているかを指摘しています。 doquery関数はAFAICTを冗長化したものです。あなたが問題の範囲をあまり狭く見ていればそれは問題ありませんが、私の答えが最も単純な方法で説明していることを知っている限り、なぜポスターが働いていないのかがうまくいかないのです。 –