el problema está en la forma en que se utiliza el gestor de contexto. Llamar al doquery
simplemente crea un objeto de administrador de contexto; debe usarlo dentro de una declaración with
, que llama a sus métodos __enter__
y __exit__
según corresponda. Por ejemplo, pruebe lo siguiente:
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)
La salida lo que consigo es:
<contextlib._GeneratorContextManager object at 0xcf3e90>
entering
attempt 2
exiting
Es posible que desee volver a leer la documentación sobre the with statement y contextlib.
Otro problema con su código es que si c.execute
o conn.commit
lanza una excepción, c.close
no se llamará - No sé si eso es realmente necesario, pero se supone que es la razón por la que desea utilizar un gestor de contexto en lugar de una función en primer lugar. Los siguientes cambios deberían solucionar ambos problemas:
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)
Sin embargo, no creo que esta sea la forma más limpia de hacerlo. Por lo que puedo ver, no hay ninguna razón para crear tres objetos separados cursor
; puede usar el mismo para cada consulta. No creo que la llamada al conn.commit
sea realmente necesaria. Usar la conexión de la base de datos como gestor de contexto confirmará automáticamente las transacciones o las retrotraerá si se produce una excepción (ver sqlite3 module documentation).
EDITAR: Esta es una versión mucho más limpia, que todavía funciona. Realmente no sé lo que hace el cursor: probablemente no sea necesario (Cursor.close
ni siquiera parece estar documentado).
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)
No tiene sentido utilizar el decorador 'contextmanager' en una función normal (en oposición a un [generador] (http://docs.python.org/reference/simple_stmts.html#yield)) El objetivo de un gestor de contexto es usarlo en una declaración con, y si intenta utilizar el valor de retorno de 'doquery2' en una sentencia with, obtendrá un' TypeError'. – James
Simplemente estoy señalando dónde se rompe el código. La función doquery es AFAICT redundante. Si ve el alcance de la pregunta de forma menos estricta que yo, está bien, pero hasta donde yo sé, mi respuesta explica de la manera más simple por qué lo que el cartel menciona como no funciona no funciona. –