这应该可以工作但只是说没有股票表 - 假设在上下文管理器内某处丢失了连接?
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
答案 0 :(得分:15)
问题在于您使用上下文管理器的方式。调用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)
但是,我不认为这是最干净的方法。据我所知,没有理由创建三个单独的cursor
对象 - 您可以为每个查询使用相同的对象。我认为实际上不需要调用conn.commit
- 使用数据库连接作为上下文管理器将自动提交事务,或者在引发异常时将其回滚(参见sqlite3 module documentation)。
编辑:这是一个更清洁的版本,仍然有效。我真的不知道关闭光标实际上做了什么 - 它可能没有必要(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)
答案 1 :(得分:0)
似乎yield
干扰create table
和insert 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])