Reputation: 402423
This is not a duplicate of Why a range_iterator when a range is reversed? You'll know why if you take a look at it.
My question pertains to the distinction between a generator resulting from range
versus any other iterator, and why that distinction is made:
In [500]: iter(range(5))
Out[500]: <range_iterator at 0x10987f810>
Whereas...
In [506]: (i for i in range(4))
Out[506]: <generator object <genexpr> at 0x10a025fc0>
So, why is this distinction made and what merit does it have?
Upvotes: 3
Views: 270
Reputation: 95948
Simple, range
is not an iterator:
In [32]: r = range(10)
In [33]: next(r)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-33-0b5056469c9c> in <module>()
----> 1 next(r)
TypeError: 'range' object is not an iterator
range
is a sequence type, and like all other containers, iter
returns a corresponding iterator object:
In [34]: iter(list())
Out[34]: <list_iterator at 0x112197cc0>
In [35]: iter(tuple())
Out[35]: <tuple_iterator at 0x112107be0>
In [36]: iter(dict())
Out[36]: <dict_keyiterator at 0x1142eef98>
Generally, containers are iterables, not iterators. They implement __iter__
, whereas an iterator implements __iter__
and __next__
.
Note, the terminology in your title is a bit off, and frankly, I think you are conflating generators, iterators, and iterables. iter(obj)
doesn't return a generator, it returns an iterator object. A generator is a language construct that allows for succinctly writing iterators. The generator
objects returned by a generator function are iterators:
In [37]: def mygen():
...: yield 1
...: yield 2
...:
In [38]: g = mygen()
In [39]: g
Out[39]: <generator object mygen at 0x1121778e0>
In [40]: next(g)
Out[40]: 1
In [41]: iter(g) is g
Out[41]: True
Finally, a generator expression is just an even handier way of writing generators!
In [44]: (i for i in range(10) if i % 2 == 0)
Out[44]: <generator object <genexpr> at 0x10de89990>
In [45]: next(_)
Out[45]: 0
Upvotes: 7