問題は、コンテキストマネージャーの使用方法にあります。呼び出す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
withステートメントとcontextlibに関するドキュメントを読み直したい場合があります。
コードのもう1つの問題は、例外が発生した場合に呼び出されないことです。これが実際に必要かどう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つの別々cursor
のオブジェクトを作成する理由はありません。クエリごとに同じオブジェクトを使用できます。の呼び出しは実際には必要ないと思います。conn.commit
データベース接続をコンテキストマネージャーとして使用すると、トランザクションが自動的にコミットされるか、例外が発生した場合にロールバックされます(sqlite3モジュールのドキュメントを参照)。
編集:これははるかにクリーンなバージョンですが、それでも機能します。カーソルを閉じると実際に何が行われるのかは本当にわかりませんが、おそらく必要ではあり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)