Resetting generator object in Python
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Darkness Approaches Looping
--
Chapters
00:00 Resetting Generator Object In Python
00:30 Answer 1 Score 202
01:27 Answer 2 Score 33
01:53 Accepted Answer Score 163
02:12 Answer 4 Score 44
02:24 Thank you
--
Full question
https://stackoverflow.com/questions/1271...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #generator #yield
#avk47
ANSWER 1
Score 202
Generators can't be rewound. You have the following options:
Run the generator function again, restarting the generation:
y = FunctionWithYield() for x in y: print(x) y = FunctionWithYield() for x in y: print(x)Store the generator results in a data structure on memory or disk which you can iterate over again:
y = list(FunctionWithYield()) for x in y: print(x) # can iterate again: for x in y: print(x)
The downside of option 1 is that it computes the values again. If that's CPU-intensive you end up calculating twice. On the other hand, the downside of 2 is the storage. The entire list of values will be stored on memory. If there are too many values, that can be unpractical.
So you have the classic memory vs. processing tradeoff. I can't imagine a way of rewinding the generator without either storing the values or calculating them again.
You could also use tee as suggested by other answers, however that would still store the entire list in memory in your case, so it would be the same results and similar performance to option 2.
ACCEPTED ANSWER
Score 163
Another option is to use the itertools.tee() function to create a second version of your generator:
import itertools
y = FunctionWithYield()
y, y_backup = itertools.tee(y)
for x in y:
    print(x)
for x in y_backup:
    print(x)
This could be beneficial from memory usage point of view if the original iteration might not process all the items.
ANSWER 3
Score 44
>>> 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
ANSWER 4
Score 33
Probably the most simple solution is to wrap the expensive part in an object and pass that to the generator:
data = ExpensiveSetup()
for x in FunctionWithYield(data): pass
for x in FunctionWithYield(data): pass
This way, you can cache the expensive calculations.
If you can keep all results in RAM at the same time, then use list() to materialize the results of the generator in a plain list and work with that.