2012-07-27 56 views
4

通过在while循环中调用.fetchone()来编写以下生成器函数是否有优势?如果是这样,那么这个优势会是什么?数据库访问生成器函数是否有优势?

谢谢。

def testf2(): 
    db = connectToMysqlDB(None) 

    sql_statement = " ".join([ 
     "select d.* ", 
     "from dr_snapshot d ", 
     "order by d.PremiseID asc, d.last_read asc; "]) 

    sel_cur = db.cursor() 
    rc = sel_cur.execute(sql_statement) 

    loop_ok = True 
    while loop_ok: 
     meter_row = sel_cur.fetchone() 
     if meter_row: 
      yield meter_row 
     else: 
      loop_ok = False 

    yield None 

for read_val in testf2(): 
    print(read_val) 
    #Perform something useful other than print. 

('610159000', 6, datetime.datetime(2012, 7, 25, 23, 0), 431900L, 80598726L) 
('610160000', 6, datetime.datetime(2012, 7, 25, 23, 0), 101200L, 80581200L) 
None 
+0

可能只是说它在testf2():doSomething'中读取的时间短于完成整个语句的时间......如果它只用于一个地方而不是您真的得到很多,但如果您正在重复执行该查询在您的代码然后把它放在一个函数更符合DRY – 2012-07-27 16:08:19

+0

mySQL的python包装是否不遵循'sqlite3'模块的领导和实现光标作为iterables本身,允许您在'se__cur:'中执行'meter_row? – JAB 2012-07-27 17:17:13

+0

@JAB谢谢。这是一个好点。 – octopusgrabbus 2012-07-27 18:20:27

回答

4

没有。在功能上它们是相同的,但如果要重新使用代码,将它包装在生成器中会有好处。例如,您可以添加代码以在完成读取生成器块后关闭连接/光标。我建议您将此添加到上面的代码中,以便游标关闭。

def testf2(): 
    try: 
     db = connectToMysqlDB(None) 

     sql_statement = " ".join([ 
      "select d.* ", 
      "from dr_snapshot d ", 
      "order by d.PremiseID asc, d.last_read asc; "]) 

     sel_cur = db.cursor() 
     rc = sel_cur.execute(sql_statement) 

     loop_ok = True 
     while loop_ok: 
      meter_row = sel_cur.fetchone() 
      if meter_row: 
       yield meter_row 
      else: 
       loop_ok = False 
    except ProgrammingError: 
     print "Tried to read a cursor after it was already closed" 
    finally: 
     sel_cur.close() 

这将使重复使用更容易,因为您只需要在一个位置获得连接管理权限。

+0

生成器对象在耗尽前被垃圾收集(例如,循环体中引发异常)会发生什么?将光标包装在一个实现'__del __()'的迭代器中是不是更好? – 2012-07-27 17:26:13

+0

这是一个非常好的主意。 – Wulfram 2012-07-27 17:29:47

+0

@AndréCaronhttp://docs.python.org/release/2.5/whatsnew/pep-342.html“close()'方法的添加具有一个不明显的副作用。'close()'在生成器被垃圾收集时调用,所以这意味着生成器的代码在生成器被销毁之前获得最后一次运行机会,这最后一次机会意味着现在可以保证生成器中的try ... finally语句可以工作。 'finally'条款现在总会有机会运行。“不需要'__del __()'。 – JAB 2012-07-27 17:32:41

3

看起来我是对的,mySQL游标可迭代的(https://stackoverflow.com/a/1808414/138772)。所以,你可以做到这一点,而不是while循环(但我喜欢把数据库访问代码生成器函数里面的想法,所以只能暂时):

for meter_row in sel_cur: 
    yield meter_row 

还要注意的是,你可能不希望那最后的yield None; StopIteration异常用于指示迭代器输出已耗尽,并且for循环用作它们的标志来停止循环,所以通过包含yield None,您最终会在输出结束时将None包含在内,因为没有真正的增益。

0

使用生成器可为您在现有代码中使用结果提供额外的灵活性。例如,您可以直接将它传递给csv.writerwriterows函数。

相关问题