¿Qué son los iteradores?

Un iterador en Python se refiere a un objeto sobre el que podemos iterar. El iterador consta de valores contables, y es posible atravesar estos valores, uno por uno.
El iterador simplemente implementa el protocolo del iterador de Python. El protocolo iterador es una clase de Python que viene con dos métodos especiales, a saber __iter__()__next__()Con estos dos métodos, el iterador puede calcular el siguiente valor en la iteración.
Con los iteradores, es fácil para nosotros trabajar con secuencias de elementos en Python. No tenemos que asignar recursos de computación a todos los elementos en la secuencia, sino que iteramos en un solo elemento a la vez que nos ayuda a ahorrar espacio en la memoria.
En este artículo, estudiaremos cómo trabajar con iteradores en Python.

Objetos Iterables en Python

Un iterable es un objeto capaz de devolver un iterador. Un iterable puede representar tanto fuentes de datos finitos como infinitos. El iterable implementa directa o indirectamente los dos métodos: __iter__()__next__()El __iter__()método devuelve el objeto iterador mientras que el __next__()método nos ayuda a atravesar los elementos en el objeto iterable.
Ejemplos de objetos iterables en Python incluyen listas, diccionarios, tuplas y conjuntos.

Creando un iterador

En Python, creamos un iterador implementando los métodos __iter__()__next__()en el objeto. Considere el siguiente ejemplo:
class IterationExample:  
    def __iter__(self):
        self.x = 0
        return self

    def __next__(self):
        y = self.x
        self.x += 1
        return y

classinstance = IterationExample()  
element = iter(classinstance)  
Hemos creado un iterador llamado elementque imprime números de 0 a N. Primero creamos una instancia de la clase y le dimos el nombre classinstanceLuego llamamos al iter()método incorporado y pasamos el nombre de la instancia de la clase como parámetro. Esto crea el objeto iterador.
Discutamos ahora cómo usar un iterador para iterar realmente a través de los elementos.

Iterando a través de un iterador

El next()método nos ayuda a recorrer los elementos de un iterador. Demostrémoslo con el ejemplo dado arriba:
class IterationExample:  
    def __iter__(self):
        self.x = 0
        return self

    def __next__(self):
        y = self.x
        self.x += 1
        return y

classinstance = IterationExample()  
element = iter(classinstance)

print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
print(next(element))  
Salida
0  
1  
2  
3  
4  
5  
6  
7  
8  
9  
En el script anterior, llamamos al next()método y pasamos el nombre del elemento iterador al método como parámetro. Cada vez que hacemos esto, el iterador se mueve al siguiente elemento de la secuencia. Aquí hay otro ejemplo:
# create a list
list1 = [0, 5, 10, 15]

# create an iterator
element = iter(list1)

## use next() to traverse/iterate through the list elements

# prints first element, 0
print(next(element))

# prints second element, 5
print(next(element))

## next(element) is similar to element.__next__()

# prints third element, 10
print(element.__next__())

# prints fourth element, 15
print(element.__next__())  
Salida
0  
5  
10  
15  
En el script anterior, creamos una lista llamada list1, que tiene 4 enteros. Se elementha creado un iterador llamado El next()método nos ha ayudado a iterar a través de los elementos de la lista.

Iteración con el bucle "for"

El forbucle nos ayuda a iterar a través de cualquier objeto capaz de devolver un iterador. Por ejemplo:
# create a list
list1 = [0, 5, 10, 15]

# create an iterator
element = iter(list1)

# iterate with a for loop
for x in element:  
    print(x)
Salida
0  
5  
10  
15  
En el código anterior, creamos una variable con nombre x, que se utiliza para iterar a través del iterador a elementtravés de un forbucle.

Iteradores infinitos

Un iterador infinito es un iterador con un número infinito de iteraciones. Debemos tener mucho cuidado al tratar con iteradores infinitos. Considere el siguiente ejemplo:
class IterationExample:  
    def __iter__(self):
        self.x = 0
        return self

    def __next__(self):
        y = self.x
        self.x += 1
        return y

classinstance = IterationExample()  
element = iter(classinstance)

for x in element:  
    print(x)
El código anterior se ejecutará para siempre. Para detenerlo, tendrás que intervenir manualmente. Aquí hay otro ejemplo que muestra cómo crear un iterador infinito en Python:
class Infinite:  
    # Print all even numbers

    def __iter__(self):
        self.x = 0
        return self

    def __next__(self):
        x = self.x
        self.x += 2
        return x
El código debe devolver todos los números pares, comenzando desde 0. Podemos ejecutar el código como se muestra a continuación:
>>> y = iter(Infinite())
>>> next(y)
0  
>>> next(y)
2  
>>> next(y)
4  
>>> next(y)
6  
>>> next(y)
8  
>>> next(y)
10  
>>>
Y la cadena anterior puede continuar para siempre. Esto demuestra que con un iterador infinito, podemos tener un número infinito de elementos sin tener que almacenarlos en la memoria.
En la siguiente sección, veremos cómo podemos implementar un mecanismo para romper esos iteradores infinitos.

Deteniendo una iteración

En la sección anterior, vimos cómo crear un iterador infinito en Python. Sin embargo, los iteradores no suelen estar destinados a una iteración infinita en Python. Siempre es conveniente implementar una condición de terminación.
Podemos evitar que un iterador se ejecute para siempre con la StopIterationinstrucción. Solo necesitamos agregar una condición de terminación en el __next__()método que generará un error una vez que se haya alcanzado el número especificado de iteraciones. Aquí hay un ejemplo:
class StoppingIteration:  
    def __iter__(self):
        self.x = 1
        return self

    def __next__(self):
        if self.x <= 5:
            y = self.x
            self.x += 1
            return y
        else:
            raise StopIteration

classinstance = StoppingIteration()  
element = iter(classinstance)

for a in element:  
    print(a)
Salida
1  
2  
3  
4  
5  
La ejecución se detiene después de 5 iteraciones. Esto se debe a la self.x <= 5:condición agregada dentro del __next__()método. Si se llama al iterador después de alcanzar 5, se levantará el StopIterationevento. Considere el ejemplo dado a continuación:
class StoppingIteration:  
    def __init__(self, max = 0):
        self.max = max

    def __iter__(self):
        self.x = 1
        return self

    def __next__(self):
        if self.x <= self.max:
            val = 3 ** self.x
            self.x += 1
            return val
        else:
            raise StopIteration
Vamos a crear un iterador y luego iterar a través de él:
>>> y = StoppingIteration(3)
>>> z = iter(y)
>>> next(z)
3  
>>> next(z)
9  
>>> next(z)
27  
>>> next(z)
Traceback (most recent call last):  
  File "<pyshell#5>", line 1, in <module>
    next(z)
  File "C:\Users\admin\iteration.py", line 17, in __next__
    raise StopIteration
StopIteration  
>>>
La condición de terminación se ha implementado en la siguiente sección de nuestro código:
if self.x <= self.max:  
    val = 3 ** self.x
Pasamos un valor de 3 al iterador, lo que significa que el iterador no debe iterar más allá de 27, es decir, 3 ^ 3.

Conclusión

Los iteradores son extremadamente útiles, especialmente si necesita recorrer una gran secuencia de elementos. Los iteradores le permiten recorrer una secuencia de elementos uno por uno sin tener que cargar todos los elementos en la memoria a la vez.
En este artículo, vimos cómo crear iteradores en Python y cómo iterar a través de elementos en un iterador. También vimos cómo crear un iterador infinito y cómo agregar una condición de terminación a un iterador infinito.