Problemem jest to ze sposobu korzystania z menedżera kontekstowe. Wywołanie doquery
po prostu tworzy obiekt menedżera kontekstu - należy go użyć w ramach instrukcji with
, która wywołuje odpowiednio metody i __exit__
. Na przykład, spróbuj wykonać następujące czynności:
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)
Wyjście pojawia się:
<contextlib._GeneratorContextManager object at 0xcf3e90>
entering
attempt 2
exiting
Może chcesz ponownie przeczytać dokumentację o the with statement i contextlib.
Innym problemem z kodem jest, że jeśli c.execute
lub conn.commit
podnosi wyjątek, c.close
nie można nazwać - nie wiem, czy to jest rzeczywiście konieczne, ale prawdopodobnie jest to powód, dla którego chcesz użyć menedżera kontekstowe zamiast funkcji w pierwszej kolejności. Poniższe zmiany powinny rozwiązać oba problemy:
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)
Jednak nie sądzę, że jest to najczystszy sposób na zrobienie tego. O ile widzę, nie ma powodu, aby tworzyć trzy oddzielne obiekty cursor
- możesz użyć tego samego dla każdego zapytania. Nie sądzę, aby połączenie z conn.commit
było rzeczywiście konieczne - używając połączenia z bazą danych, ponieważ menedżer kontekstu automatycznie wykona transakcje lub wycofa je, jeśli zostanie zgłoszony wyjątek (patrz sqlite3 module documentation).
EDYCJA: Tutaj jest znacznie czystsza wersja, która nadal działa. Naprawdę nie wiem, co właściwie robi zamykanie kursora - prawdopodobnie nie jest to konieczne (Cursor.close
nawet nie wydaje się być udokumentowane).
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)
To nie ma sensu używać 'contextmanager' dekorator na normalnej funkcji (w przeciwieństwie do prądnicy [] (http://docs.python.org/reference/simple_stmts.html#yield)). Cały punkt menedżera kontekstu polega na użyciu go w instrukcji with, a jeśli spróbujesz użyć zwracanej wartości 'doquery2' w instrukcji with, otrzymasz" TypeError ". – James
Ja tylko wskazuję, gdzie kod się psuje. Funkcja doquery jest nadmiarowa. Jeśli widzisz, że zakres pytania jest mniej zawężony niż ja, to w porządku, ale o ile wiem, moja odpowiedź wyjaśnia w najprostszy sposób, dlaczego to, co ten plakat mówi, że nie działa, nie działa. –