How does range object allow for multiple iterations while generators object do not?
A range object is a plain iterable sequence, while a generator is also an iterator.
The difference between the two is that an iterable is used to generate iterators which store the iteration state. This can be seen if we play around with range
, its iterators, and next
a bit.
First, we can see that range is not an iterator if we try to call next
on it
In [1]: next(range(0))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [1], in <module>
----> 1 next(range(0))
TypeError: 'range' object is not an iterator
We can create the iterator ourselves by calling the iter
builtin, we can see that this gives us a different iterator type when called on our range.
In [2]: iter(range(0))
Out[2]: <range_iterator at 0x28573eabc90>
Each of the iterators created by the iterable will store its own iteration state (say, an index into the range object that's incremented every time it's advanced) so we can use them independently
In [3]: range_obj = range(10)
In [4]: iterator_1 = iter(range_obj)
In [5]: iterator_2 = iter(range_obj)
In [6]: [next(iterator_1) for _ in range(5)] # advance iterator_1 5 times
Out[6]: [0, 1, 2, 3, 4]
In [7]: next(iterator_2) # left unchanged, fetches first item from range_obj
Out[7]: 0
Python also creates iterators by itself when a for loop is used, which can be seen if we take a look at instructions generator for it
In [8]: dis.dis("for a in b: ...")
1 0 LOAD_NAME 0 (b)
2 GET_ITER
>> 4 FOR_ITER 4 (to 10)
6 STORE_NAME 1 (a)
8 JUMP_ABSOLUTE 4
>> 10 LOAD_CONST 0 (None)
12 RETURN_VALUE
Here, the GET_ITER
is the same as doing iter(b)
.
Now with the generator, after creating it by calling the generator function, Python gives you an iterator directly, as there's no iterable object above it to be generated from. Calling the generator function could be seen as calling iter(...)
, but passing it everything is left up to the user
as arguments to the function instead of fetching the information from an object it was created by.