¿Cómo funciona Yield en Python?

Para entender cómo funciona yield en Python, primero debemos entender que es un generador. Y antes de entender cómo funciona un generador, debes entender los iterables.

¡Empezemos entoces por los iterables!

Iterables

El iterable más sencillo de entender sería una lista: [0, 1, 2 ]. La lista la podemos leer elemento a elemento haciendo un sencillo for:

>>> my_list = [0, 1, 2]
>>> for i in my_list:
...    print(i)
0
1
2

my_list es un iterable, al igual, cuando usamos List comprehensions o comprensión de listas, creamos una lista que es un iterable.

>>> my_list = [x for x in range(3)]
>>> for i in my_list:
...    print(i)
0
1
2

Todos los elementos que podemos recorrer son un iterable.

¿Qué problemas presentan? Son fáciles de gestionar pero en caso de grandes cantidades de datos ocupan mucho espacio en memoria… y no siempre queremos usar todos los valores!

Y aquí es donde aparecen los generadores y nuestro querido yield.

Generadores

Los generadores son iterables, aunque algo diferente al que hemos visto más arriba. Sólo se pueden iterar una vez y no almacenan valores en memoria, los generan «en el momento» en el que son llamados.

>>> my_generator = (x for x in range(3))
>>> for i in my_generator:
...    print(i)
0
1
2

La única diferencia es que usamos ( ) en lugar de [ ]. NO podemos volver a ejecutar for i in my_generator: una segunda vez, el generador calcula el 0, luego olvida el valor y calcula el 1, y terminan calculando el 2 (uno a uno).

¿Y dónde entra en juego Yield?

Yield como return

Yield es la palabra reservada que se usa como return, a menos que la función devuelva otro generador.

En el caso anterior range(3) es un generador que genera una secuencia desde 0 hasta 3 (sin incluir este último), por eso no hemos visto la palabra yield por ninguna parte.

Pero si desgranáramos este generador, entonces quedaría algo así:

>>> def my_generator():
...    my_list = range(3)
...    for i in my_list:
...        yield i

Ahora realiza la misma función y vemos claramente la función del yield. Devuelve el valor y detiene la ejecución de esta pieza de código hasta la siguiente iteración.

>>> generador = my_generator()
>>> print(generador) # generador is an object!
<generator object create_generator at 0xb7555c34>

Debemos entender que el código no es ejecutado dónde es escrito, sino es almancenado en un objeto generador y es llamado una vez lo recorremos.

>>> for i in generador:
...     print(i)
0
1
2

¡Espero que este artículo te ayude optimizar tu código y ahorrar memoria!

Déjame en los comentarios dónde crees que sería útil utilizar en los generadores, ¡te leo en los comentarios!