Python3 Iteradores y Generadores


Iteradores

La iteración es una de las funciones más potentes de Python y es una forma de acceder a los elementos de una colección.

Un iterador es un objeto que puede recordar la posición de un recorrido.

El objeto iterador comienza a acceder desde el primer elemento de la colección y termina cuando se han visitado todos los elementos. El iterador solo puede avanzar, no retroceder.

El iterador tiene dos métodos básicos:iter()ynext()。

Los objetos de tipo cadena, lista o tupla se pueden usar para crear iteradores:

Ejemplo (Python 3.0+)

>>> list=[1,2,3,4]
>>> it = iter(list)    # Crear el objeto iterador
>>> print (next(it))   # Imprimir el siguiente elemento del iterador
1
>>> print (next(it))
2
>>>

Los objetos iteradores se pueden recorrer usando la sentencia for habitual:

Ejemplo (Python 3.0+)

#!/usr/bin/python3 list=[1,2,3,4] it = iter(list) # Crear el objeto iterador for x in it: print (x, end=" ")

Al ejecutar el programa anterior, el resultado es el siguiente:

1 2 3 4

También se puede usar la función next():

Ejemplo (Python 3.0+)

#!/usr/bin/python3 import sys # Importar el módulo sys list=[1,2,3,4] it = iter(list) # Crear el objeto iterador while True: try: print (next(it)) except StopIteration: sys.exit()

Al ejecutar el programa anterior, el resultado es el siguiente:

1
2
3
4

Crear un iterador

Para usar una clase como iterador, es necesario implementar dos métodos en la clase: __iter__() y __next__().

Si ya conoces la programación orientada a objetos, sabrás que una clase tiene un constructor; el constructor de Python es __init__(), que se ejecuta cuando el objeto se inicializa.

Para más información, consulta:Python3 Programación Orientada a Objetos

El método __iter__() devuelve un objeto iterador especial. Este objeto iterador implementa el método __next__() y señala la finalización de la iteración mediante la excepción StopIteration.

El método __next__() (en Python 2 es next()) devuelve el siguiente objeto del iterador.

Crea un iterador que devuelve números, con valor inicial 1 e incremento de 1 en 1:

Ejemplo (Python 3.0+)

class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): x = self.a self.a += 1 return x myclass = MyNumbers() myiter = iter(myclass) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter))

El resultado de la ejecución es:

1
2
3
4
5

StopIteration

La excepción StopIteration se usa para indicar la finalización de la iteración, evitando así bucles infinitos. En el método __next__() podemos configurar que, después de completar un número determinado de iteraciones, se lance la excepción StopIteration para terminar la iteración.

Detener la ejecución después de 20 iteraciones:

Ejemplo (Python 3.0+)

class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): if self.a <= 20: x = self.a self.a += 1 return x else: raise StopIteration myclass = MyNumbers() myiter = iter(myclass) for x in myiter: print(x)

El resultado de la ejecución es:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Generadores

En Python, las funciones que utilizanyieldse denominan generadores (generator).

yieldes una palabra clave utilizada para definir funciones generadoras. Una función generadora es un tipo especial de función que puede producir valores gradualmente durante la iteración, en lugar de devolver todos los resultados de una sola vez.

A diferencia de las funciones normales, un generador es una función que devuelve un iterador y solo se puede usar para operaciones de iteración. Para entenderlo más fácilmente, un generador es un iterador.

Cuando se usa la sentenciayielden una función generadora, la ejecución de la función se pausa y el valor de la expresión que sigue ayieldse devuelve como valor de la iteración actual.

Luego, cada vez que se llama al métodonext()o se usa el bucleforpara iterar, la función continúa desde donde se pausó la última vez, hasta que de nuevo se encuentra con la sentenciayieldDe esta manera, la función generadora puede producir valores paso a paso sin necesidad de calcular y devolver todos los resultados de una sola vez.

Al llamar a una función generadora, se devuelve un objeto iterador.

A continuación se muestra un ejemplo sencillo que demuestra el uso de funciones generadoras:

Ejemplo

def countdown(n): while n > 0: yield n n -= 1 # Crear el objeto generador generator = countdown(5) # Obtener valores iterando el generador print(next(generator)) # Salida: 5 print(next(generator)) # Salida: 4 print(next(generator)) # Salida: 3 # Usar un bucle for para iterar el generador for value in generator: print(value) # Salida: 2 1

En el ejemplo anterior, la funcióncountdownes una función generadora. Utiliza la sentencia yield para producir gradualmente los números descendentes de n a 1. Cada vez que se ejecuta la sentencia yield, la función devuelve el valor descendente actual y, en la siguiente llamada, continúa desde donde se pausó.

Al crear un objeto generador y usar la función next() o un bucle for para iterar el generador, podemos obtener gradualmente los valores producidos por la función generadora. En este ejemplo, primero usamos la función next() para obtener los primeros tres valores descendentes y luego usamos un bucle for para obtener los dos restantes.

La ventaja de las funciones generadoras es que pueden generar valores bajo demanda, evitando generar grandes cantidades de datos de una sola vez y ocupar mucha memoria. Además, los generadores pueden combinarse sin problemas con otras herramientas de iteración (como el bucle for), proporcionando una forma de iteración simple y eficiente.

Al ejecutar el programa anterior, el resultado es el siguiente:

5
4
3
2
1

El siguiente ejemplo utiliza yield para implementar la secuencia de Fibonacci:

Ejemplo (Python 3.0+)

#!/usr/bin/python3 import sys def fibonacci(n): # Función generadora: Fibonacci a, b, counter = 0, 1, 0 while True: if (counter > n): return yield a a, b = b, a + b counter += 1 f = fibonacci(10) # f es un iterador devuelto por el generador while True: try: print (next(f), end=" ") except StopIteration: sys.exit()

Al ejecutar el programa anterior, el resultado es el siguiente:

0 1 1 2 3 5 8 13 21 34 55
Otras extensiones