Iterables
El protocolo Iterable
se usa por tipos donde es posible procesar sus contenidos de uno en uno, en orden. Un Iterable
es un objeto que va a proporcionar un Iterator
que puede ser usado para realizar este proceso.
Hay muchos tipos iterables en Python incluyendo List, Sets, Dictionaries, Tuples, etc. Todos ellos son contenedores iterables que pueden proporcionar un iterador.
Para ser un tipo iterable solo es necesario implementar el método __iter__()
(que es el único método en el protocolo Iterable
). Este método debe proporcionar una referencia al objeto Iterator
. Esta referencia puede ser al propio tipo de dato o puede ser a cualquier otro tipo que implemente el protocolo Iterator
.
Iteradores¶
Un iterador es un objeto que devuelve una secuencia de valores. Esta secuencia puede ser finita en longitud o infinita (aunque mucho iteradores orientados a contenedores proporcionan un conjunto fijo de valores).
El protocolo Iterator
especifica el método __next__()
. Este método debe devolver el siguiente elemento de la secuencia o lanzar una excepción de tipo StopIteration
para indicar que no existen más valores.
Como ejemplo vamos a crear una clase llamada Pares
que devuelva números pares entre 0 y un determinado límite. Este nuevo tipo va a actuar simultaneamente como Iterable
y como Iterator
al implementar ambos protocolos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Pares
con un bucle for
:
1 2 3 |
|
Resultado:
0, 2, 4, 6, 8, 10, Fin
Generadores¶
Un generador (Generator) es una función especial que se puede usar para generar una secuencia de valores para iterar sobre ellos a demanda. Para ello se utiliza la palabra clave yield
. Esta palabra clave solo se puede utilizar dentro de una función o de un método.
Cuando se ejecuta yield
, la ejecución de la función se suspende y se devuelve el valor que acompaña a yield
. La ejecución de la función generador se activará de nuevo en la siguiente llamada a partir del punto donde se suspendió.
Veamos un ejemplo sencillo:
1 2 3 4 5 6 7 8 |
|
Resultado:
1, 2, 3, Fin
La función genera_numeros
es una función especial que devuelve un objeto Generator
que es el que genera los valores requeridos para la iteración del for.
Veamos el mismo ejemplo enriquecido para que nos aclare el orden de ejecución de las cosas:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Resultado:
Inicio
1
Siguiente
2
Otro mas
3
Termino
Fin
Vamos a reescribir el ejemplo del iterable de Pares
utilizando una función que actue como generador. Veremos que queda mucho más sencillo.
1 2 3 4 5 6 7 8 9 |
|
Resultado:
0, 2, 4, 6, 8, 10, Fin
No es necesario un bucle for
para trabajar con una función generador. El objeto generador que devuelve la función soporta la función next
. Esta función se invoca pasando como argumento el objeto generador y devuelve el siguiente valor de la secuencia.
1 2 3 4 5 6 7 8 9 10 |
|
Resultado:
0
2
4
Corutinas¶
Las corutinas (coroutines) son muy similares a los generadores pero con una diferencia: los generadores producen datos y las corutinas los consumen. Las corutinas son funciones que mediante la palabra clave yield
esperan en ese punto a recibir un dato que les es suministrado mediante la función send()
.
Es necesario iniciar la corutina al principio con un next
o con un send(None)
. Esto avanza la corutina hasta el yield
donde se queda esperando a recibir datos para procesarlos.
Una corutina se ejecuta indefinidamente hasta que se le manda un close()
. Se puede capturar el momento en que una corutina recibe el close
capturando la excepción GeneratorExit
y escribiendo código para ella.
Veamos un ejemplo que examina si los textos enviados a una corutina contienen una determinada palabra:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Resultado:
Inicio
Buscando palabra Python
Python es muy sencillo
Saliendo de la corutina
Fin