Iterador y Generador

PythonBeginner
Practicar Ahora

Introducción

En este laboratorio, aprenderemos sobre los Iteradores, Generadores y Expresiones Generadoras integrados en Python. Veremos cómo estos constructos se pueden utilizar para escribir código eficiente y elegante en Python.

Logros

  • Iterador
  • Generador
  • Expresión Generadora

Iterador

Un iterador es un objeto sobre el que se puede iterar (recorrer en un bucle). Un objeto que devolverá datos, uno a la vez. En Python, un iterador se crea a partir de un objeto iterable, como una lista, una tupla o una cadena.

Abra un nuevo intérprete de Python.

python3

Para crear un iterador en Python, debemos implementar dos métodos en nuestro objeto: __iter__ y __next__.

__iter__ devuelve el objeto iterador en sí mismo. El método __next__ devuelve el siguiente valor del iterador. Si no hay más elementos para devolver, debe generar una excepción StopIteration.

A continuación, se muestra un ejemplo de un iterador simple que recorre una lista de números:

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        ## len() es el número de elementos de la lista
        if self.index >= len(self.data):
            raise StopIteration
        result = self.data[self.index]
        self.index += 1
        return result

iterator = MyIterator([1, 2, 3, 4, 5])
for x in iterator:
    print(x)

Salida:

1
2
3
4
5

Los iteradores son útiles porque nos permiten acceder a los elementos de un iterable uno a la vez, en lugar de cargar todos los elementos en memoria a la vez. Esto puede ser especialmente útil cuando se trabaja con conjuntos de datos grandes que no caben en memoria.

Los iteradores también se utilizan para implementar la evaluación diferida en Python. Esto significa que los elementos de un iterador solo se generan cuando se necesitan, en lugar de generar todos los elementos de una vez. Esto puede ser un enfoque más eficiente, ya que nos permite evitar generar y almacenar elementos innecesarios.

Si desea obtener un elemento a la vez de un iterador, puede utilizar la función next(). Esta función devolverá el siguiente elemento del iterador. Si no hay más elementos, generará una excepción StopIteration.

iterator = MyIterator([1, 2, 3, 4, 5])
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

## StopIteration
print(next(iterator))

Salida:

1
2
3
4

## StopIteration

A continuación, se presentan algunos casos de uso comunes de los iteradores en Python:

  1. Recorrer los elementos de un conjunto de datos grande, uno a la vez.
  2. Implementar la evaluación diferida de un conjunto de datos grande.
  3. Implementar la lógica de iteración personalizada en una clase.
  4. Los iteradores son una herramienta poderosa en Python y se pueden utilizar para escribir código eficiente y elegante.

Generador

Un generador es un tipo especial de iterador que se crea utilizando una función. Es una forma sencilla de crear un iterador utilizando una función.

Una función generadora se define como una función regular, pero en lugar de utilizar la palabra clave return para devolver un valor, utiliza la palabra clave yield. Cuando se llama a la función generadora, no ejecuta el cuerpo de la función inmediatamente. En lugar de eso, devuelve un objeto generador que se puede utilizar para ejecutar el cuerpo de la función a demanda.

La función generadora puede tener una declaración yield en cualquier parte de su cuerpo. Cuando se llama a la función generadora, no ejecuta el cuerpo de la función inmediatamente. En lugar de eso, devuelve un objeto generador que se puede utilizar para ejecutar el cuerpo de la función a demanda.

A continuación, se muestra un ejemplo de una función generadora que genera los cuadrados de una lista de números:

def my_generator(data):
    for x in data:
        yield x**2

for x in my_generator([1, 2, 3, 4, 5]):
    print(x)

Salida:

1
4
9
16
25

Los generadores son útiles porque nos permiten generar elementos a demanda, en lugar de generar todos los elementos de una vez. Esto puede ser un enfoque más eficiente, ya que nos permite evitar generar y almacenar elementos innecesarios.

Los generadores también se utilizan para implementar la evaluación diferida en Python. Esto significa que los elementos de un generador solo se generan cuando se necesitan, en lugar de generar todos los elementos de una vez. Esto puede ser un enfoque más eficiente, ya que nos permite evitar generar y almacenar elementos innecesarios.

A continuación, se presentan algunos casos de uso comunes de los generadores en Python:

  1. Generar elementos a demanda, en lugar de generar todos los elementos de una vez.
  2. Implementar la evaluación diferida de un conjunto de datos grande.
  3. Implementar la lógica de iteración personalizada en una función.
  4. Los generadores son una herramienta poderosa en Python y se pueden utilizar para escribir código eficiente y elegante.

Diferencias entre Iterador y Generador

La principal diferencia entre un iterador y un generador es la forma en que se implementan.

Un iterador es un objeto que implementa dos métodos: __iter__ y __next__. El método __iter__ devuelve el objeto iterador en sí mismo, y el método __next__ devuelve el siguiente valor del iterador.

Un generador es una función que utiliza la palabra clave yield para devolver un valor. Cuando se llama a la función generadora, no ejecuta el cuerpo de la función inmediatamente. En lugar de eso, devuelve un objeto generador que se puede utilizar para ejecutar el cuerpo de la función a demanda.

A continuación, se presenta un resumen de las principales diferencias entre iteradores y generadores:

  1. Los iteradores son objetos que implementan los métodos __iter__ y __next__. Se crean a partir de objetos iterables, como listas, tuplas o cadenas.
  2. Los generadores son funciones que utilizan la palabra clave yield para devolver un valor. Se crean llamando a una función generadora.
  3. Los iteradores se pueden implementar utilizando una clase, mientras que los generadores se implementan utilizando una función.
  4. Los iteradores devuelven un elemento a la vez, mientras que los generadores devuelven un objeto generador que se puede utilizar para generar elementos a demanda.
  5. Los iteradores se utilizan para acceder a los elementos de un objeto iterable uno a la vez, mientras que los generadores se utilizan para generar elementos a demanda.

En general, tanto los iteradores como los generadores son herramientas útiles para iterar sobre una secuencia de elementos en Python. Nos permiten acceder o generar los elementos de una secuencia uno a la vez, lo que puede ser más eficiente que generar todos los elementos de una vez.

Ejemplo avanzado: Generador de números primos

En este ejemplo, crearemos un generador que genera números primos.

Primero, definamos una función auxiliar _is_prime que devuelva True si un número es primo y False en caso contrario:

def _is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

Ahora, definamos nuestra función generadora prime_numbers:

def prime_numbers(n):
    for i in range(2, n+1):
        if _is_prime(i):
            yield i

Probemos nuestro generador:

for prime in prime_numbers(20):
    print(prime)

Salida:

2
3
5
7
11
13
17
19

Expresión Generadora

Una expresión generadora es similar a una comprensión de lista, pero en lugar de crear una lista, devuelve un objeto generador.

Una expresión generadora se define utilizando paréntesis (), y puede incluir una o más cláusulas for. Se evalúa a demanda y devuelve un objeto generador que se puede utilizar para generar los elementos de la expresión a demanda.

A continuación, se muestra un ejemplo de una expresión generadora que genera los cuadrados de una lista de números:

generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in generator:
    print(x)

Salida:

1
4
9
16
25

Las expresiones generadoras son útiles porque nos permiten generar elementos a demanda, en lugar de generar todos los elementos de una vez. Esto puede ser un enfoque más eficiente, ya que nos permite evitar generar y almacenar elementos innecesarios.

Las expresiones generadoras también se utilizan para implementar la evaluación diferida en Python. Esto significa que los elementos de una expresión generadora solo se generan cuando se necesitan, en lugar de generar todos los elementos de una vez. Esto puede ser un enfoque más eficiente, ya que nos permite evitar generar y almacenar elementos innecesarios.

A continuación, se presentan algunos casos de uso comunes de las expresiones generadoras en Python:

  1. Generar elementos a demanda, en lugar de generar todos los elementos de una vez.
  2. Implementar la evaluación diferida de un conjunto de datos grande.
  3. Escribir código conciso y eficiente.

Las expresiones generadoras son una herramienta poderosa en Python y se pueden utilizar para escribir código eficiente y elegante.

Expresión generadora y comprensión de listas

A continuación, se muestra un ejemplo de una comprensión de lista y una expresión generadora que generan los cuadrados de una lista de números:

## Comprensión de lista
squares = [x**2 for x in [1, 2, 3, 4, 5]]
print(squares)

## Expresión generadora
squares_generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in squares_generator:
    print(x)

Salida:

[1, 4, 9, 16, 25]
1
4
9
16
25

Existen varias similitudes y diferencias entre las comprensiones de lista y las expresiones generadoras:

Similitudes

  1. Tanto las comprensiones de lista como las expresiones generadoras se utilizan para generar una secuencia de elementos.
  2. Ambos utilizan la misma sintaxis, con una o más cláusulas for y una expresión para generar los elementos.

Diferencias

  1. Una comprensión de lista genera una lista, mientras que una expresión generadora genera un objeto generador.
  2. Una comprensión de lista genera todos los elementos de la lista de una vez, mientras que una expresión generadora genera los elementos a demanda.
  3. Una comprensión de lista utiliza más memoria, ya que almacena todos los elementos en una lista, mientras que una expresión generadora utiliza menos memoria, ya que genera los elementos a demanda.
  4. Una comprensión de lista suele ser más rápida en la ejecución, ya que genera todos los elementos de una vez, mientras que una expresión generadora suele ser más lenta, ya que genera los elementos a demanda.

En general, tanto las comprensiones de lista como las expresiones generadoras son herramientas útiles para generar una secuencia de elementos en Python. Las comprensiones de lista suelen ser más rápidas y utilizan más memoria, mientras que las expresiones generadoras suelen ser más lentas y utilizan menos memoria. Cuál utilizar depende de los requisitos específicos de la aplicación.

Resumen

En este laboratorio, aprendimos sobre los Iteradores, Generadores y Expresiones Generadoras integradas en Python. Vimos cómo estos constructos se pueden utilizar para escribir código eficiente y elegante en Python. También vimos un ejemplo de cómo utilizar generadores para implementar un generador de números primos.