Apuntes_Python/01_curso/Modulo_2/2-4c_generadores.py
2022-12-24 22:41:20 -03:00

157 lines
3.6 KiB
Python

"""
GENERADORES
Son funciones especiales, devuelven una secuencia de valores, de uno en uno,
en cada llamada. Son una forma sencilla y potente de iterador.
Un iterador tiene siempre sus elementos disponibles, un generador los va GENERANDO con
un llamado al metodo __next()__
Con esto se pueden tener generadores, que generen infitinos elementos, ej. nums. naturares.
Retornan un resultado con la sentencia "yield". Y la funcion queda en "pausa" hasta q es
invocada nuevamente.
Creación
Un grador. de nros. 0-9
"""
def gen_10():
for n in range(10):
yield n
"""
Uso, hay q instanciar el generador. Luego usar la funcion __next__()
al no quedar elementos q iterar, el grador. lanza una excepción StopIteration
"""
a_gen_10 = gen_10() # Instancia del generador
a_gen_10.__next__() # 0
a_gen_10.__next__() # 1
a_gen_10.__next__() # 2
#a_gen_10.__next__() # 3...9
#a_gen_10.__next__() # Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration
"""
Al igual q los iteradores, los gradores. se pueden "consumir" con un ciclo for
Ej. Generador nros. naturales, infinitos.
"""
def naturales():
n = 1
while True:
yield n
n += 1
nats = naturales()
"""
Ej. Sentencia "return" para indicar StopIteration
"""
def n_nats(n):
i = 1
while True:
if i > n:
return
yield i
i += 1
# Generar los 100 primeros naturales
"""
SIMILAR a las listas por comprensión, solo q estas se escriben entre parntesis
# Lista por Comprensión
"""
[p for p in range(10) if p % 2 == 0] # [0, 2, 4, 6, 8]
# Generador
(p for p in range(10) if p % 2 == 0) # <generator object <genexpr> at 0x7fb25c49da50>
"""
UTILES:
- Trabajando con estructuras infinitas.
- Trabajando con estructuras con alta carga de memoria, se puede reducir el espacio a revisar
- Se puede retrasar su calculo hasta último momento para favorecer otros procesos
Se puede pasar un argumento en caso de acabar el generador.
"""
next(nats, None)
next(nats, 'ratas')
# Envio de objetos a un generador con send() (tb lo hace "generar")
def accumulator():
total = 0
value = None
while True:
# receive sent value
value = yield total
if value is None:
break
# aggregate values
total += value
generator = accumulator()
# advance until the first "yield"
next(generator) # 0
# from this point on, the generator aggregates values
generator.send(1) # 1
generator.send(10) # 11
generator.send(100) # 111
# Calling next(generator) is equivalent to calling generator.send(None)
# next(generator) # StopIteration
# USO DE GENERADORES
def gen1000():
"Genera los pri meros 1000 números."
for x in range(1000):
yield x
primeros1000 = gen1000()
for x in primeros1000:
print(x, end=" - ")
print('\n'*2)
def gen_primos(cantidad=1):
"Generador de números primos."
cont = 1
ls_prim = []
# Comienzo ciclo infinito
while cantidad > cont:
es_primo = True
cont += 1
if len(ls_prim) > 0:
for primo in ls_prim:
if cont % primo == 0:
es_primo = False
break
if es_primo:
ls_prim.append(cont)
yield cont
prim1000prim = gen_primos(1000)
for x in prim1000prim:
print(x, end=" - ")
def num_par(n):
return (x for x in range(n) if x % 2 == 0)
gen = num_par(15)
gen.__next__()
gen.__next__()
print(list(gen))