Los ge­ne­ra­do­res de Python son un tipo especial de función en Python. Generan valores paso por paso y permiten así un uso eficiente de la memoria.

¿Qué son exac­ta­me­n­te los Python ge­ne­ra­to­rs?

Los Python ge­ne­ra­to­rs son funciones es­pe­cia­les que devuelven un iterator en Python. La forma de crear Python ge­ne­ra­to­rs es similar a la de­fi­ni­ción de una función normal. La di­fe­re­n­cia está en los detalles: en lugar de una sentencia return, los ge­ne­ra­do­res tienen una sentencia yield. Además, las funciones ge­ne­ra­do­ras como los iterators también im­ple­me­n­tan una función next().

Nota

Los Python ge­ne­ra­to­rs pe­r­te­ne­cen a conceptos avanzados de la pro­gra­ma­ción en Python. Si tus co­no­ci­mie­n­tos ya van más allá de los tu­to­ria­les de Python para pri­n­ci­pia­n­tes, puedes echar un vistazo a estos artículos:

La palabra clave yield

Si ya tienes ex­pe­rie­n­cia con otros lenguajes de pro­gra­ma­ción o con Python, entonces conoces la sentencia return. Se utiliza para pasar los valores ca­l­cu­la­dos por las funciones a la instancia de llamada en el código del programa. Una vez alcanzada la sentencia return de una función, se sale de la función y finaliza su ejecución. Si es necesario, se puede volver a llamar la función.

Con yield es diferente: esta palabra sustituye a return en los Python ge­ne­ra­to­rs. Si ahora llamas a tu generador, se devolverá el valor que pases a la sentencia yield. Después, sin embargo, no se sale de un Python generator, sino que este si­m­ple­me­n­te se in­te­rru­m­pe. El estado actual de la función del generador se guarda. Cuando vuelvas a abrir la función de tu generador, saltarás a la posición guardada.

Usos de los ge­ne­ra­do­res de Python

Dado que los ge­ne­ra­do­res de Python funcionan según el principio de “Lazy Eva­lua­tion” y solo se evalúan los valores cuando son realmente ne­ce­sa­rios, las funciones ge­ne­ra­do­ras son perfectas para trabajar con grandes ca­n­ti­da­des de datos.

Una función normal cargaría primero todo el contenido del fichero en una variable y, por tanto, en la memoria. Con grandes ca­n­ti­da­des de datos, tu memoria local podría no ser su­fi­cie­n­te y el proceso llevaría a un Me­mo­r­yE­rror. Los ge­ne­ra­do­res facilitan la re­so­lu­ción de estos problemas leyendo el fichero línea por línea. La palabra clave yield devuelve el valor que necesitas en ese momento e in­te­rru­m­pe la ejecución de la función hasta la siguiente llamada a la función, que procesa otra línea del fichero.

Consejo

Muchas apli­ca­cio­nes web requieren el pro­ce­sa­mie­n­to de grandes ca­n­ti­da­des de datos. Python también es adecuado para su uso en proyectos web. Con Deploy Now puedes acelerar la creación de tus proyectos web be­ne­fi­ciá­n­do­te del de­s­plie­gue y la co­n­s­tru­c­ción au­to­má­ti­cos a través de GitHub.

Sin embargo, no solo el manejo de grandes ca­n­ti­da­des de datos, sino también el trabajo con infinitos se ve eno­r­me­me­n­te fa­ci­li­ta­do por los Python ge­ne­ra­to­rs. Dado que la memoria local es finita, los ge­ne­ra­do­res son la única forma de crear listas infinitas o similares en Python.

Lectura de archivos CSV con Python ge­ne­ra­to­rs

Como ya se ha me­n­cio­na­do, los ge­ne­ra­do­res son es­pe­cia­l­me­n­te adecuados para trabajar con grandes ca­n­ti­da­des de datos. El siguiente programa permite leer un archivo CSV línea por línea de forma eficiente para la memoria:

import csv
def leer_csv (nombredefichero):
 with open(nombredefichero, 'r') as fichero:
  tmp = csv.reader(fichero)
  for línea in tmp:
   yield línea
for línea in leer_csv ('test.csv'):
 print(línea)
Python

En el ejemplo de código, primero se importa el módulo csv para tener acceso a las funciones de Python para procesar archivos CSV. Luego se puede ver la de­fi­ni­ción de un generador de Python llamado “leer_csv”, que comienza con la palabra clave “def” como las de­fi­ni­cio­nes de funciones. Después de abrir el archivo, el bucle for en Python recorre el archivo línea por línea. Cada línea se devuelve con la palabra clave “yield”.

Fuera de la función ge­ne­ra­do­ra, las líneas devueltas por el Python Generator se imprimen una tras otra en la consola. Para ello se utiliza la función Python print.

Creación de es­tru­c­tu­ras de datos infinitas con Python Ge­ne­ra­to­rs

Ló­gi­ca­me­n­te, una es­tru­c­tu­ra de datos infinita no puede al­ma­ce­nar­se lo­ca­l­me­n­te en el ordenador. Sin embargo, las es­tru­c­tu­ras de datos infinitas son ese­n­cia­les para algunas apli­ca­cio­nes. Las funciones ge­ne­ra­do­ras también ayudan en este caso, ya que procesan todos los elementos uno tras otro y así no inundan la memoria. Este podría ser un ejemplo de una secuencia infinita de números naturales en código Python:

def numeros_naturales():
 n = 0
 while True:
  yield n
  n += 1
for cifra in numeros_naturales():
 print(cifra)
Python

En primer lugar, se define un Python generator llamado “numeros_naturales”, que establece el valor inicial de la variable “n”. A co­n­ti­nua­ción, se inicia un bucle while en Python que se ejecuta sin fin. Con “yield” se devuelve el valor actual de la variable y se in­te­rru­m­pe la ejecución de la función ge­ne­ra­do­ra. Si se abre la función por segunda vez, el número pre­via­me­n­te emitido se in­cre­me­n­ta­rá en 1 y el generador se ejecutará de nuevo hasta que el in­té­r­pre­te encuentre la palabra clave “yield”. En el bucle for, que se encuentra debajo de la función ge­ne­ra­do­ra, se devuelven los números generados. Si el programa no se in­te­rru­m­pe ma­nua­l­me­n­te, se ejecutará sin fin.

Abre­via­tu­ra de los ge­ne­ra­do­res en Python

Con List Co­m­prehe­n­sio­ns puedes crear listas de Python en una sola línea de código. Existe una abre­via­tu­ra similar para los ge­ne­ra­do­res. A co­n­ti­nua­ción se presenta un generador que in­cre­me­n­ta con valor 1 los números del 0 al 9. Es similar al generador utilizado para generar la secuencia infinita de números naturales.

def numeros_naturales():
 n = 0
 while n <= 9:
  yield n
  n+=1
Python

Si deseas escribir este generador en una línea de código, utiliza una sentencia for entre pa­ré­n­te­sis como en el siguiente ejemplo:

increment_generator = (n + 1 for n in range(10))
Python

Si ahora quieres devolver este generador, obtendrás el siguiente resultado:

<generator object <genexpr> at 0x0000020CC5A2D6C8>

De este modo se muestra en qué lugar de la memoria se encuentra el objeto generador creado. Para acceder al resultado de tu generador, puedes utilizar la función next():

print(next(increment_generator))
print(next(increment_generator))
print(next(increment_generator))
Python

Esta sección de código pro­po­r­cio­na el siguiente resultado, donde los números de 0 a 2 se han in­cre­me­n­ta­do cada uno en 1:

1
2
3

Ge­ne­ra­do­res vs. List Co­m­prehe­n­sio­ns

La abre­via­tu­ra de los ge­ne­ra­do­res es muy similar a la de los List Co­m­prehe­n­sio­ns. La única di­fe­re­n­cia visual radica en los corchetes: mientras que para las Co­m­prehe­n­sio­ns se utilizan corchetes, para la creación de ge­ne­ra­do­res de Python se utilizan pa­ré­n­te­sis. Pero in­te­r­na­me­n­te hay una di­fe­re­n­cia mucho más si­g­ni­fi­ca­ti­va: los re­qui­si­tos de memoria de los ge­ne­ra­do­res son mucho menores que los de las listas.

import sys
listadeincremento = [n + 1 for n in range(100)]
generadordeincremento = (n + 1 for n in range(100))
print(sys.getsizeof(listadeincremento))
print(sys.getsizeof(generadordeincremento))
Python

El programa anterior calcula las ne­ce­si­da­des de memoria de la lista y del generador equi­va­le­n­te:

912
120

Mientras que la lista requiere 912 bytes, el generador solo necesita 120. Esta di­fe­re­n­cia va au­me­n­ta­n­do conforme in­cre­me­n­ta la cantidad de datos que hay que procesar.

Ir al menú principal