Iteradores de Python (__iter__ y __next__): ¿Cómo usarlo y por qué?

Los iteradores son objetos sobre los que se puede iterar. En este tutorial, aprenderá cómo funciona el iterador y cómo puede construir su propio iterador usando los métodos __iter__ y __next__.

Video: iteradores de Python

Iteradores en Python

Los iteradores están en todas partes en Python. Están elegantemente implementados dentro de forbucles, comprensiones, generadores, etc. pero están ocultos a la vista.

Iterator en Python es simplemente un objeto sobre el que se puede iterar. Un objeto que devolverá datos, un elemento a la vez.

Técnicamente hablando, un objeto iterador de Python debe implementar dos métodos especiales __iter__()y __next__(), colectivamente, llamado protocolo iterador .

Un objeto se llama iterable si podemos obtener un iterador de él. La mayoría de los contenedores integrados en Python como: lista, tupla, cadena, etc.son iterables.

La iter()función (que a su vez llama al __iter__()método) devuelve un iterador de ellos.

Iterando a través de un iterador

Usamos la next()función para iterar manualmente a través de todos los elementos de un iterador. Cuando lleguemos al final y no haya más datos para devolver, se generará la StopIterationExcepción. A continuación se muestra un ejemplo.

 # define a list my_list = (4, 7, 0, 3) # get an iterator using iter() my_iter = iter(my_list) # iterate through it using next() # Output: 4 print(next(my_iter)) # Output: 7 print(next(my_iter)) # next(obj) is same as obj.__next__() # Output: 0 print(my_iter.__next__()) # Output: 3 print(my_iter.__next__()) # This will raise error, no items left next(my_iter)

Salida

 4 7 0 3 Traceback (última llamada más reciente): Archivo "", línea 24, en el siguiente (my_iter) StopIteration

Una forma más elegante de iterar automáticamente es mediante el ciclo for. Con esto, podemos iterar sobre cualquier objeto que pueda devolver un iterador, por ejemplo, lista, cadena, archivo, etc.

 >>> for element in my_list:… print(element)… 4 7 0 3

Trabajo de bucle for para iteradores

Como vemos en el ejemplo anterior, el forbucle pudo iterar automáticamente a través de la lista.

De hecho, el forbucle puede iterar sobre cualquier iterable. Echemos un vistazo más de cerca a cómo forse implementa realmente el ciclo en Python.

 for element in iterable: # do something with element

En realidad, se implementa como.

 # create an iterator object from that iterable iter_obj = iter(iterable) # infinite loop while True: try: # get the next item element = next(iter_obj) # do something with element except StopIteration: # if StopIteration is raised, break from loop break

Entonces, internamente, el forbucle crea un objeto iterador, iter_objllamando iter()al iterable.

Irónicamente, este forbucle es en realidad un bucle while infinito.

Dentro del ciclo, llama next()para obtener el siguiente elemento y ejecuta el cuerpo del forciclo con este valor. Después de que todos los elementos se agotan, StopIterationse levanta el que se atrapa internamente y termina el lazo. Tenga en cuenta que cualquier otro tipo de excepción pasará.

Creación de iteradores personalizados

Construir un iterador desde cero es fácil en Python. Solo tenemos que implementar __iter__()los __next__()métodos y.

El __iter__()método devuelve el objeto iterador en sí. Si es necesario, se puede realizar alguna inicialización.

El __next__()método debe devolver el siguiente elemento de la secuencia. Al llegar al final, y en convocatorias posteriores, debe subir StopIteration.

Aquí, mostramos un ejemplo que nos dará la siguiente potencia de 2 en cada iteración. El exponente de potencia comienza desde cero hasta un número establecido por el usuario.

Si no tiene ninguna idea sobre la programación orientada a objetos, visite Programación orientada a objetos de Python.

 class PowTwo: """Class to implement an iterator of powers of two""" def __init__(self, max=0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration # create an object numbers = PowTwo(3) # create an iterable from the object i = iter(numbers) # Using next to get to the next iterator element print(next(i)) print(next(i)) print(next(i)) print(next(i)) print(next(i))

Salida

 1 2 4 8 Traceback (última llamada más reciente): Archivo "/home/bsoyuj/Desktop/Untitled-1.py", línea 32, en letra impresa (siguiente (i)) Archivo "", línea 18, en __siguiente__ subir StopIteration StopIteration

También podemos usar un forbucle para iterar sobre nuestra clase de iterador.

 >>> for i in PowTwo(5):… print(i)… 1 2 4 8 16 32

Iteradores infinitos de Python

No es necesario que el elemento de un objeto iterador deba agotarse. Puede haber infinitos iteradores (que nunca termina). Debemos tener cuidado al manejar este tipo de iteradores.

Aquí hay un ejemplo simple para demostrar iteradores infinitos.

La función de función iter()incorporada se puede llamar con dos argumentos donde el primer argumento debe ser un objeto invocable (función) y el segundo es el centinela. El iterador llama a esta función hasta que el valor devuelto es igual al centinela.

 >>> int() 0 >>> inf = iter(int,1) >>> next(inf) 0 >>> next(inf) 0

Podemos ver que la int()función siempre devuelve 0. Así que pasarla como iter(int,1)devolverá un iterador que llama int()hasta que el valor devuelto sea igual a 1. Esto nunca sucede y obtenemos un iterador infinito.

También podemos construir nuestros propios iteradores infinitos. El siguiente iterador, teóricamente, devolverá todos los números impares.

 class InfIter: """Infinite iterator to return all odd numbers""" def __iter__(self): self.num = 1 return self def __next__(self): num = self.num self.num += 2 return num

Una ejecución de muestra sería la siguiente.

 >>> a = iter(InfIter()) >>> next(a) 1 >>> next(a) 3 >>> next(a) 5 >>> next(a) 7

Y así…

Tenga cuidado de incluir una condición de terminación al iterar sobre estos tipos de iteradores infinitos.

La ventaja de utilizar iteradores es que ahorran recursos. Como se muestra arriba, podríamos obtener todos los números impares sin almacenar todo el sistema numérico en la memoria. Podemos tener elementos infinitos (teóricamente) en una memoria finita.

Existe una forma más sencilla de crear iteradores en Python. Para obtener más información, visite: generadores de Python que utilizan yield.

Articulos interesantes...