简体   繁体   English

如何在Python中返回生成器

[英]How to return a generator in Python

I am thinking of designing my function to return a generator in combination with a database query. 我正在考虑设计我的函数以结合数据库查询返回生成器。 but having some question on the concept of iterators 但是对迭代器的概念有疑问

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

The question is, when i iterate the generator in the main function, i hit "error on closed cursor". 问题是,当我在主函数中迭代生成器时,我碰到“关闭的游标错误”。 Should i close or not close it in the func()? 我应该在func()中关闭还是不关闭它? I suppose when the call to func() end, the dbconn variable will be out of scope and i shouldn't have to worry about closing? 我想当对func()的调用结束时,dbconn变量将超出范围,我不必担心关闭吗?

 # main function
 for it in func():
     do something with it
 close dbconn here?

How should i design this? 我应该如何设计? returing a data structure like a list would be better? 重现像列表这样的数据结构会更好吗? thanks 谢谢

You could use a Context Manager , eg (contains some pseudocode): 您可以使用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

Of course this is only useful if execute(sql) really returns a generator. 当然,这仅在execute(sql)确实返回生成器时才有用。 If you put all data into a list (and thus into memory) before closing the connection, your problem will be obsolete. 如果在关闭连接之前将所有数据放入列表(进而放入内存),则问题将过时。

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

In response to your comment: 针对您的评论:

If your database adapter follows the python DB API spec, an efficient way is to fetch a bunch of rows with fetchmany multiple times. 如果您的数据库适配器遵循python DB API规范,则一种有效的方法是使用fetchmany多次提取一堆行。

The following code fetches rows in chunks of 100, and explicitly calls dbclose when the execution leaves the with block: 以下代码以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)

I don't have much experience on using database, but I think you should retrieve the query results and return it as a list. 我在使用数据库方面经验不足,但是我认为您应该检索查询结果并将其作为列表返回。 If you really need an iterator (but I cant see why), then return an iterator over the list ret : 如果您确实需要一个迭代器(但我看不到为什么),则在列表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 

Now, if their exist a way to retrieve the nth element of a query, something like execute(sql, n) which return None if n is too big, then you could use a yield: 现在,如果它们存在一种检索查询的第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()

Now, this is not what I would recommend, mainly because the connection to the db stays open while the iterator is not finished. 现在,这不是我所建议的,主要是因为在迭代器未完成时与数据库的连接保持打开状态。 And it might never happens if something fails or is badly designed. 如果某件事失败或设计不当,它可能永远不会发生。

You cannot try to manipulate a cursor after you have closed the database connection I will try with this approach: 关闭数据库连接后,您将无法尝试操作游标,我将尝试使用这种方法:

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/ (*) http://www.python.org/dev/peps/pep-0249/

I hope it helps 希望对您有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM