Restablecer el objeto generador en Python
Tengo un objeto generador devuelto por rendimiento múltiple. La preparación para llamar a este generador requiere bastante tiempo. Por eso quiero reutilizar el generador varias veces.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
Por supuesto, estoy pensando en copiar el contenido en una lista simple. ¿Hay alguna manera de restablecer mi generador?
Ver también: ¿Cómo mirar hacia adelante un elemento (vistazo) en un generador de Python?
Los generadores no se pueden rebobinar. Tienes las siguientes opciones:
Ejecute nuevamente la función del generador, reiniciando la generación:
y = FunctionWithYield() for x in y: print(x) y = FunctionWithYield() for x in y: print(x)
Almacene los resultados del generador en una estructura de datos en la memoria o en el disco que pueda iterar nuevamente:
y = list(FunctionWithYield()) for x in y: print(x) # can iterate again: for x in y: print(x)
La desventaja de la opción 1 es que vuelve a calcular los valores. Si eso consume mucha CPU, terminarás calculando dos veces. Por otro lado, la desventaja del 2 es el almacenamiento. La lista completa de valores se almacenará en la memoria. Si hay demasiados valores, esto puede resultar poco práctico.
Así que tenemos el clásico equilibrio entre memoria y procesamiento . No puedo imaginar una forma de rebobinar el generador sin almacenar los valores o calcularlos nuevamente.
También podría usar tee
lo sugerido por otras respuestas; sin embargo, eso aún almacenaría la lista completa en la memoria en su caso, por lo que serían los mismos resultados y un rendimiento similar a la opción 2.
Otra opción es utilizar la itertools.tee()
función para crear una segunda versión de tu generador:
import itertools
y = FunctionWithYield()
y, y_backup = itertools.tee(y)
for x in y:
print(x)
for x in y_backup:
print(x)
Esto podría ser beneficioso desde el punto de vista del uso de la memoria si la iteración original no procesara todos los elementos.
>>> def gen():
... def init():
... return 0
... i = init()
... while True:
... val = (yield i)
... if val=='restart':
... i = init()
... else:
... i += 1
>>> g = gen()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.send('restart')
0
>>> g.next()
1
>>> g.next()
2