2012-07-27 54 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函數。

相關問題