8

これは機能するはずですが、株式テーブルがないと言っているだけです-コンテキストマネージャー内のどこかで接続が失われたと思われますか?

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
4

2 に答える 2

16

問題は、コンテキストマネージャーの使用方法にあります。呼び出す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.commitc.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)
于 2012-04-07T19:41:34.557 に答える
0

とステートメントyieldに干渉しているようです。create tableinsert into

yield以下では、を除いては使用せず、問題なくselect動作します。

#!/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])
于 2012-04-05T14:00:38.223 に答える