Iterador personalizado de Python

Salman Mehmood 21 junio 2023
  1. Cree un iterador personalizado con la ayuda de los métodos de clase __iter__ y __next__ en Python
  2. Cree un iterador personalizado con la ayuda de un generador en Python
Iterador personalizado de Python

En este artículo, aprenderemos qué son los iteradores y cómo crear un iterador personalizado con la ayuda de métodos de clase llamados __iter__ y __next__. También aprenderemos a crear un iterador personalizado con la ayuda de un generador en Python.

Cree un iterador personalizado con la ayuda de los métodos de clase __iter__ y __next__ en Python

Ahora, cuando diga iterador, como sugiere el nombre, se usará para iteraciones.

Si vienes de lenguajes como C, C++ y Java, tenemos un concepto común: ++. Un concepto más es conocido cuando hablamos de iteraciones: el bucle for.

Digamos que tenemos una lista de 5 elementos. Usando un bucle, podemos iterar desde el primer elemento hasta el último elemento.

De esta manera, tenemos una lista con algunos valores, y si queremos imprimirlos uno por uno usando los números de índice.

Si pasamos nuestro índice 0, nos dará el primer elemento, 2. Si usamos el valor de índice 1, nos dará el segundo elemento, 5.

Numbers = [2, 5, 10, 3, 1]

print(Numbers[0])
print(Numbers[1])

Producción :

2
5

La otra forma de imprimir los valores es usando un bucle. Podemos iterar en una lista e imprimir todos los valores.

Numbers = [2, 5, 10, 3, 1]

for i in Numbers:
    print(i)

Producción :

2
5
10
3
1

Ahora tenemos una forma más: mediante el uso de un iterador. En general, un iterador trabaja detrás de la escena del bucle for.

Usaremos una función iter() para entender un iterador, que convertirá nuestra lista en un iterador. Este iterador no nos dará todos los valores; solo dará un valor a la vez.

Numbers = [2, 5, 10, 3, 1]

ITR = iter(Numbers)
print(ITR)

Podemos ver que está imprimiendo el objeto de un iterador.

<list_iterator object at 0x000001FEDCFF7310>

Pero, si desea el valor, puede establecer ITR.__next__(), que es un método incorporado. Nos dará el primer valor durante la primera llamada del método __next__() sobre un objeto en particular; esta es la misma forma en que usamos el valor del índice, pero la ventaja es que no tenemos que usar el valor del índice.

umbers = [2, 5, 10, 3, 1]

ITR = iter(Numbers)
print(ITR.__next__())

Producción :

2

Dará el siguiente valor cuando ejecutemos ITR.__next__() en la siguiente línea de ejecución. Detrás de esta escena, el iterador tendrá múltiples valores, por lo que cuando llamamos al método __next__(), tomará un valor.

Y nuevamente, cuando llamamos a __next__(), conoce el último valor de i, lo que significa que conservará el estado del último valor. Esa es la belleza del iterador; cuando volvamos a llamar a la función, conservará el valor anterior.

Podemos crear nuestro iterador usando una clase. Usando la clase, podemos imprimir los 10 valores principales individualmente.

Para lograrlo, tendremos una clase llamada TopValues, y en esta clase especificaremos un contador.

Y para ello utilizaremos la función __init__, donde definiremos nuestra variable contador self.N=1; obviamente, el contador se inicializará desde 1. Necesitamos dos métodos importantes para crear nuestro iterador personalizado.

El primero es el método iter(), que nos dará el objeto del iterador, y luego el método next() nos proporcionará el siguiente valor u objeto. En el método __next__, en lugar de devolver self.N, usamos una variable llamada VALUE en la que asignaremos self.N, y en la siguiente línea incrementamos self.N en uno .

Para que se incremente en cada siguiente iteración y luego devuelva el VALOR no self.N porque self.N se incrementa para la siguiente iteración.

class TopValues:
    def __init__(self):
        self.N = 1

    def __iter__(self):
        return self

    def __next__(self):
        VALUE = self.N
        self.N += 1
        return VALUE

Nuestro iterador ya está listo para que podamos crear un objeto de la clase TopValues. Usemos un bucle porque, por lo general, cuando tienes un iterador, podemos usar bucles.

T_Val = TopValues()

for i in T_Val:
    print(i)

Ahora, cuando ejecutamos este código, obtenemos miles de valores.

1
2
3
4
5
....
1000

Tratemos de entender lo que está pasando. Para verificar si el iterador está funcionando, usaremos __next__().

print(T_Val.__next__())

Esta línea imprimirá el valor de la primera iteración, 1, pero ¿qué falla en el bucle? El problema es que el ciclo irá de principio a fin; asumimos que el final sería 10, pero no hemos mencionado dónde debemos detenernos.

Cuando usamos un bucle, llamará a la función next(), que es como funciona. Los bucles for utilizan internamente la función next(); por lo tanto, llamará a esta función next() una y otra vez.

Debemos aplicar una condición dentro del método __next__.

También tenemos que configurar el bloque else; dentro de este bloque, generaremos una excepción. De lo contrario, después de imprimir 10 valores, el bucle imprimirá valores Ninguno.

class TopValues:
    def __init__(self):
        self.N = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.N <= 10:
            VALUE = self.N
            self.N += 1
            return VALUE
        else:
            raise StopIteration


T_Val = TopValues()

for i in T_Val:
    print(i)

Producción :

1
2
3
4
5
6
7
8
9
10

Cree un iterador personalizado con la ayuda de un generador en Python

Ahora intentemos crear una función generadora que haga lo mismo. Los generadores son mucho más simples ya que se encargan de los métodos __iter__() y __next__() por nosotros.

Si intentamos escribir una función generadora llamada Our_Gen(), pasaremos el parámetro dentro de esta función.

Dado que estamos recorriendo la lista, obtenemos un elemento a la vez. Y, cuando no haya más elementos para recorrer, automáticamente se encargará de eso y generará una excepción para detener la iteración.

Ahora estamos recorriendo la función Our_Gen(), por lo que debería imprimir un elemento a la vez. Si ejecutamos eso, podemos ver que obtenemos uno a la vez.

def Our_Gen(n_list):
    for i in n_list:
        yield i


Func = Our_Gen([2, 5, 10, 3, 1])

for i in Func:
    print(i)

Producción :

2
5
10
3
1

Para asegurarnos de que nuestra función next() sigue funcionando. Después de ejecutar, obtenemos cinco elementos, obtenemos una excepción de iteración de parada.

def Our_Gen(n_list):
    for i in n_list:
        yield i


Func = Our_Gen([2, 5, 10, 3, 1])

print(Func.__next__())
print(Func.__next__())
print(Func.__next__())
print(Func.__next__())
print(Func.__next__())
print(Func.__next__())

Producción :

2
5
10
3
1
Traceback (most recent call last):
  File "c:\Users\Dell\Desktop\demo\demo.py", line 56, in <module>
    print(Func.__next__())
StopIteration

Los generadores son más fáciles de escribir que las clases. Pero dependiendo de su caso de uso, es posible que necesite saber cómo hacer el método __iter__() y el método __next__() en una clase.

Puede leer más sobre cómo crear un iterador personalizado desde aquí.

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Artículo relacionado - Python Iterator