- Insert a line
result = []
at the start of the function. - Replace each
yield expr
withresult.append(expr)
. - Insert a line
return result
at the bottom of the function. - Yay - no more
yield
statements! Read and figure out the code. - Compare the function to the original definition.
Don't confuse your Iterablesiterables, Iteratorsiterators, and Generatorsgenerators
So the generator object is sort of like an adapter - at one end it exhibits the iterator protocol, by exposing __iter__()
and next()
methods to keep the for
loop happy. At the other end, however, it runs the function just enough to get the next value out of it, and puts it back in suspended mode.
Why Use Generatorsuse generators?
Usually, you can write code that doesn't use generators but implements the same logic. One option is to use the temporary list 'trick' I mentioned before. That will not work in all cases, for e.g. if you have infinite loops, or it may make inefficient use of memory when you have a really long list. The other approach is to implement a new iterable class SomethingIterSomethingIter
that keeps the state in instance members and performs the next logical step in its next()
(or __next__()
in Python 3) method. Depending on the logic, the code inside the next()
method may end up looking very complex and prone to bugs. Here generators provide a clean and easy solution.