Python: cómo usar un generador para evitar problemas de memoria SQL

Tengo el siguiente método que accede a la base de datos mysql y la consulta se ejecuta en un servidor al que no tengo acceso para cambiar nada respecto al aumento de memoria. Soy nuevo en generadores y comencé a leer más sobre esto y pensé que podría convertirlo en un generador de uso.

def getUNames(self): globalUserQuery = ur'''SELECT gu_name FROM globaluser WHERE gu_locked = 0''' global_user_list = [] try: self.gdbCursor.execute(globalUserQuery) rows = self.gdbCursor.fetchall() for row in rows: uName = unicode(row['gu_name'], 'utf-8') global_user_list.append(uName) return global_user_list except Exception, e: traceback.print_exc() 

Y utilizo este código de la siguiente manera:

 for user_name in getUNames(): ... 

Este es el error que recibí del servidor:

 ^GOut of memory (Needed 725528 bytes) Traceback (most recent call last): ... packages/MySQLdb/connections.py", line 36, in defaulterrorhandler raise errorclass, errorvalue OperationalError: (2008, 'MySQL client ran out of memory') 

¿Cómo debo usar el generador para evitar esto?

 while true: self.gdbCursor.execute(globalUserQuery) row = self.gdbCursor.fetchone() if row is None: break yield row 

No estoy seguro si lo anterior es el camino correcto ya que estoy esperando una lista como resultado de mi método de base de datos. Creo que lo que sería genial es obtener un fragmento de la consulta y devolver una lista, y una vez que el generador de finalización dé el siguiente conjunto, siempre y cuando la consulta devuelva resultados.

Con MySQLdb, el cursor predeterminado carga todo el conjunto de resultados en una lista de Python cuando se realiza la llamada a cursor.execute(..) . Para una consulta grande que puede causar un error de memoria, use o no un generador.

En su lugar, utilice un SSCursor o SSDictCursor. Estos mantendrán el conjunto de resultados en el lado del servidor y le permitirán interactuar a través de los elementos en el conjunto de resultados en el lado del cliente:

 import MySQLdb import MySQLdb.cursors as cursors def getUNames(self): # You may of course want to define `self.gdbCursor` somewhere else... conn = MySQLdb.connect(..., cursorclass=cursors.SSCursor) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # Set the cursor class to SSCursor here self.gdbCursor = conn.cursor() globalUserQuery = ur'''SELECT gu_name FROM globaluser WHERE gu_locked = 0''' try: self.gdbCursor.execute(globalUserQuery) for row in self.gdbCursor: uName = unicode(row['gu_name'], 'utf-8') yield uName except Exception, e: traceback.print_exc() 

No hay mucha documentación sobre la diferencia entre el Cursor predeterminado y el SSCursor . La mejor fuente que conozco es la documentación de las clases de Cursor Mixin:

El cursor predeterminado usa un CursorStoreResultMixIn :

 In [2]: import MySQLdb.cursors as cursors In [8]: print(cursors.CursorStoreResultMixIn.__doc__) This is a MixIn class which causes the entire result set to be stored on the client side, ie it uses mysql_store_result(). If the result set can be very large, consider adding a LIMIT clause to your query, or using CursorUseResultMixIn instead. 

y el SSCursor usa un CursorUseResultMixIn :

 In [9]: print(cursors.CursorUseResultMixIn.__doc__) This is a MixIn class which causes the result set to be stored in the server and sent row-by-row to client side, ie it uses mysql_use_result(). You MUST retrieve the entire result set and close() the cursor before additional queries can be peformed on the connection. 

Desde que cambié getUNames en un generador, se usaría así:

 for row in self.getUnames(): ...