Search code examples
pythonpython-3.xiteratorgeneratortypeerror

If range() is a generator in Python 3.3, why can I not call next() on a range?


Perhaps I've fallen victim to misinformation on the web, but I think it's more likely just that I've misunderstood something. Based on what I've learned so far, range() is a generator, and generators can be used as iterators. However, this code:

myrange = range(10)
print(next(myrange))

gives me this error:

TypeError: 'range' object is not an iterator

What am I missing here? I was expecting this to print 0, and to advance to the next value in myrange. I'm new to Python, so please accept my apologies for the rather basic question, but I couldn't find a good explanation anywhere else.


Solution

  • range is a class of immutable iterable objects. Their iteration behavior can be compared to lists: you can't call next directly on them; you have to get an iterator by using iter.

    So no, range is not a generator.

    You may be thinking, "why didn't they make it an iterator"? Well, ranges have some useful properties that wouldn't be possible that way:

    • They are immutable, so they can be used as dictionary keys.
    • They have the start, stop and step attributes (since Python 3.3), count and index methods and they support in, len and __getitem__ operations.
    • You can iterate over the same range multiple times.

    >>> myrange = range(1, 21, 2)
    >>> myrange.start
    1
    >>> myrange.step
    2
    >>> myrange.index(17)
    8
    >>> myrange.index(18)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: 18 is not in range
    >>> it = iter(myrange)
    >>> it
    <range_iterator object at 0x7f504a9be960>
    >>> next(it)
    1
    >>> next(it)
    3
    >>> next(it)
    5