[英]How to return a generator in Python
我正在考虑设计我的函数以结合数据库查询返回生成器。 但是对迭代器的概念有疑问
def func():
sql =" select some rows "
dbconn = "connect and open to dtabase code"
ret = ( execute(sql) ) <----- returning a generator?
dbclose <---- I close the db connection here, but it gives me error
return ret
问题是,当我在主函数中迭代生成器时,我碰到“关闭的游标错误”。 我应该在func()中关闭还是不关闭它? 我想当对func()的调用结束时,dbconn变量将超出范围,我不必担心关闭吗?
# main function
for it in func():
do something with it
close dbconn here?
我应该如何设计? 重现像列表这样的数据结构会更好吗? 谢谢
您可以使用Context Manager ,例如(包含一些伪代码):
from contextlib import contextmanager
@contextmanager
def func():
sql =" select some rows "
dbconn = "connect and open to dtabase code"
yield execute(sql) # returns this really a generator?
dbclose #pseudocode, you probably want to put this in a try/finally block
with func() as result:
for it in result:
do something with it
当然,这仅在execute(sql)
确实返回生成器时才有用。 如果在关闭连接之前将所有数据放入列表(进而放入内存),则问题将过时。
def func():
sql =" select some rows "
dbconn = "connect and open to dtabase code"
ret = list( execute(sql) )
dbclose # no problem here, since all data is already fetched
return ret
针对您的评论:
如果您的数据库适配器遵循python DB API规范,则一种有效的方法是使用fetchmany
多次提取一堆行。
以下代码以100个块为单位获取行,并在执行离开with
块时显式调用dbclose
:
def cursor_iter(cursor, num_of_rows=100):
while True:
rows = cursor.fetchmany(num_of_rows)
if not rows: break
for row in rows:
yield row
@contextmanager
def func():
sql = "select some rows"
dbconn = connect_and_open_database()
cursor = dbconn.cursor()
cursor.execute(sql)
yield cursor_iter(cursor)
dbclose()
with func() as result:
for row in result:
do_something(row)
我在使用数据库方面经验不足,但是我认为您应该检索查询结果并将其作为列表返回。 如果您确实需要一个迭代器(但我看不到为什么),则在列表ret
返回一个迭代器:
def func():
sql =" select some rows "
dbconn = "connect and open to dtabase code"
ret = execute(sql) # a list
dbclose()
return (elmt for elmt in ret) # return an iterator over ret
现在,如果它们存在一种检索查询的第n个元素的方法,例如execute(sql, n)
,如果n
太大则返回None
,则可以使用yield:
def func():
sql =" select some rows "
dbconn = "connect and open to dtabase code"
n = 0
ret = execute(sql,n) # return the n-th element
while ret is not None:
yield ret
n += 1
ret = execute(sql,n)
dbclose()
现在,这不是我所建议的,主要是因为在迭代器未完成时与数据库的连接保持打开状态。 如果某件事失败或设计不当,它可能永远不会发生。
关闭数据库连接后,您将无法尝试操作游标,我将尝试使用这种方法:
def func(params):
sql = "query to execute"
cursor = execute(sql, params)
return cursor.fetchall() # retrieves all posible results as a sequence of sequences,
# i.g. list of tuples(*)
### Main ###
# Open database connection
# Create cursor
for elem in func(): # Call to retrieve desired element's method and do something with that
# Do something
# Close cursor
# Close database connection
(*) http://www.python.org/dev/peps/pep-0249/
希望对您有所帮助
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.