We saw what an iterator is in the previous recipe; now in this one, let's see how to generate an iterator.
Generators provide a clean syntax to loop through a sequence of values eliminating the need to have the two functions, __iter__ and next(). We don't have to write a class. A point to note is that both generators and iterables produce an iterator.
Let's have a look the following example; it should be easy to follow if you understood comprehension from the previous section. In this case, we have a generator comprehension. If you recall, we tried doing a tuple comprehension in this way and got a generator object:
SimpleCounter = (x**2 for x in range(1,10)) tot = 0 for val in SimpleCounter: tot+=val print tot
It should be clear that the preceding code snippet will find the sum of the squares of a given range; in this case, the range is 1 to 9. (The range function in Python is right-ended.) Using a generator, we created an iterator called SimpleCounter
and we use it in a for
loop in order to access the underlying data sequentially. Note that we have not used the iter()
function here. Notice how clean the code is. We successfully recreated our old SimpleCounter
class in a very elegant manner.
Let's look at how to use the yield statement to create a generator:
def my_gen(low,high): for x in range(low,high): yield x**2 tot = 0 for val in my_gen(1,10): tot+=val print tot
In the preceding example, the my_gen()
function is a generator; we used the yield statement to return the output in a sequence.
In the previous section, we mentioned that both a generator and iterables produce an iterator. Let's validate this by trying to call the generator using the iter
function:
gen = (x**2 for x in range(1,10)) for val in iter(gen): print val
Before we move on to iterables in our next recipe, a key point to note with a generator is that once we have gone through the sequence, we are done—no more data.