24. Corrutinas

Las corrutinas son similares a los generadores pero tienen ciertas diferencias. Las principales son las siguientes:

  • Los generadores son productores de datos
  • Las corrutinas son consumidores de datos

Antes de nada, vamos a revisar como se creaba un generador. Podemos hacerlo de la siguiente manera:

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a+b

A modo de breve recordatorio, el uso de yield retorna de la función a donde fue llamada, pero si es vuelta a llamar continúa su ejecución inmediatamente después del yield.

Ahora podemos usarlo en un bucle for como se muestra a continuación:

for i in fib():
    print(i)

Es rápido y no consume demasiada memoria ya que genera los valores al vuelo (uno a uno) en vez de almacenarlos todos en una lista. Ahora, si usamos yield en el anterior ejemplo, tendremos una corrutina. Las corrutinas consumen los valores que le son enviados. Un ejemplo muy sencillo sería un grep en Python:

def grep(pattern):
    print("Buscando", pattern)
    while True:
        line = (yield)
        if pattern in line:
            print(line)

Pero espera, ¿qué es lo que devuelve yield? Bueno, en realidad lo que hemos hecho es convertirlo en una corrutina. No contiene ningún valor inicialmente, sino que proporcionamos esos valores externamente. Los valores son proporcionados usando el método .send(). Aquí podemos ver un ejemplo:

search = grep('coroutine')
next(search)
# Salida: Buscando coroutine
search.send("I love you")
search.send("Don't you love me?")
search.send("I love coroutines instead!")
# Salida: I love coroutines instead!

Por lo tanto cuando enviamos una línea con .send(), si cumple con el pattern o patrón que hemos indicado al llamar a la función grep() será impresa por pantalla.

Los valores enviados son accedidos por yield. Tal vez te preguntes sobre el uso de next(). Es requerido para empezar la corutina. Al igual que los generators, las corutinas no empiezan inmediatamente, sino que se ejecutan en respuesta a los métodos __next__() y .send(). Por lo tanto tienes que ejecutar next() para que la ejecución avance hasta la expresión yield.

Por otro lado, podemos cerrar la corrutina llamando al método .close() como se muestra a continuación:

search = grep('coroutine')
# ...
search.close()

Las coroutines van mucho más allá de lo que hemos explicado, por lo que te sugerimos que eches un vistazo a estar increíble presentación http://www.dabeaz.com/coroutines/Coroutines.pdf de David Beazley (en Inglés).