lunes, 14 de julio de 2025

13 Python 3.13. Generadores

 Los generadores extraen valores de una función, que se van a almacenar en objetos iterables. Esto quiere decir que estos objetos los vamos a poder recorrer con un bucle, iteradores o el método next.

Estos valores se almacenan de uno en uno y permanecen en un estado de espera (pausa o suspensión de estado) hasta que se solicita el siguiente.
En una función en lugar de utilizar la instrucción return utilizamos la instrucción yield.

En una función tradicional que construya una lista de valores, la instrucción return nos devuelve todos los valores.

Pero la instrucción yield, sólo nos devuelve el primer valor y se mantiene a la espera.
El control de flujo retorna a la llamada a la función y la llamar de nuevo a la función nos retorna el segundo valor, por eso decimos que se generan objetos iterables.

La diferencia estriba en que se nos devuelven los valores uno a uno, dentro de un objeto iterable.

- Las ventajas:

- Es más eficiente que una función tradicional, menos consumo de memoria y recursos.
- Para trabajar con listas de valores infinitos.

- La sintaxis es la misma que en una función tradicional, en lugar de utilizar la instrucción return, utilizamos la instrucción yield.

 - yield from se utiliza para "descomprimir" un iterador o generador dentro de otro generador. Permite integrar iteraciones anidadas de manera más limpia y concisa.

yield devuelve un valor individual y pausa la función.

yield from itera sobre un iterable o generador, pasando sus valores al llamador.
yield from simplifica la iteración anidada y el manejo de generadores dentro de generadores

yield:

Ideal para crear iteradores que generan valores bajo demanda, ahorrando memoria al no tener que almacenar todos los valores en una lista. Ejemplos: generar secuencias infinitas, procesar archivos grandes línea por línea.
yield from:
Útil para componer generadores a partir de otros generadores o iterables, como unir secuencias, procesar datos de múltiples fuentes

 # Función tradicional pasando un argumento maxNum

def numeros(maxNum):

                num=1

                numLista=[]

                while num<=maxNum:

                               if num%2==0:

                                               numLista.append(num)

                               num=num+1

                #print(numLista)

                return numLista

print(numeros(10))

# Lo mismo con un Generador.

def numeros(maxNum):

                num=1

# Ya no necesito la lista.

                while num<=maxNum:

                               if num%2==0:

                                               # No necesitamos añadir nada a la lista

                                               # Utilizamos Yield

                                               yield num

                               num=num+1

                #print(numLista)

                # No necesitamos la instrucción Return.

                # return numLista

 

# Necesito fuera de la función crear un variable que almacene el objeto iterable que devuelve la función.

resultado=numeros(10)

 

# Una vez que tenemos el objeto iterable, lo recorremos con un bucle for o while.

for i in resultado:

                print(i)

 

resultado2=numeros(10)

# El método next nos devuelve valor a valor el contenido del objeto.

print(f"Valor 1º: {next(resultado2)}")

print(f"Valor 2º: {next(resultado2)}")

print(f"Valor 3º: {next(resultado2)}")

# En este ejemplo podemos ver como a cada llamada el control de flujo vuelve al objeto iterable.

# Entre una llamada y otra el objeto generador está en pausa o estado de suspensión.

# Mientras que en una función tradicional se genera la lista completa, en este caso no.

# Podemos generar valores infinitos, sólo en el caso de que las llamadas sean infinitas.

 #############

 # Instrucción yield from, la utilizamos para simplificar código si utilizamos bucles anidados, un for dentro de un for.

# Similar a recorrer un array bidimensional.

 # Cuando colocamos un * delante del argumento indicamos que nos sabemos cuantos elementos recibirá.

# Y que estos elementos los recibe en forma de TUPLA.

def Elementos(*elemento):

for i in elemento:

yield i

 # Creo el elemento contenedor y paso los argumentos.

resultado=Elementos("Madrid", "A Coruña", "Barcelona", "Ibiza")

 # Utilizamo next para recorrer los valores.

print(f"Ciudad 1ª: {next(resultado)}")

print(f"Ciudad 2ª: {next(resultado)}")

print(f"Ciudad 3ª: {next(resultado)}")

print("")

# Tenemos los elementos y queremos acceder a los subelementos que son las letras.

def Elementos2(*elemento2):

for i in elemento2:

yield i

for a in i:

yield a

 # Creo el elemento contenedor y paso los argumentos.

resultado2=Elementos2("Madrid", "A Coruña", "Barcelona", "Ibiza")

 
# Utilizamo next para recorrer los valores.

print(f"Ciudad 1ª: {next(resultado2)}")

print(f"Letra 1ª: {next(resultado2)}")

print(f"Letra 2ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Ciudad 2ª: {next(resultado2)}")

print("")

 ##############

 # Utilizamos Yield From para simplificar el código.

def Elementos2(*elemento2):

for i in elemento2:

# Prescindimos del bucle anidado.

#for a in i:

yield from i

 # Creo el elemento contenedor y paso los argumentos.

resultado2=Elementos2("Madrid", "A Coruña", "Barcelona", "Ibiza")

 
# Utilizamo next para recorrer los valores.

print(f"Letra 1ª: {next(resultado2)}")

print(f"Letra 2ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print(f"Letra 3ª: {next(resultado2)}")

print("")

 ##############

def Sumarn(n):

v = num = 6

for i in range(n):

yield num

num += 1

print(f"Numero: {i+v}")

print(f"Suma Numeros: {sum(Sumarn(5))}")


No hay comentarios:

Publicar un comentario

Gracias por vuestros aportes.