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

33,106

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 directly iterable"? 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
Share:
33,106

Related videos on Youtube

Jeff
Author by

Jeff

Updated on April 28, 2020

Comments

  • Jeff
    Jeff about 4 years

    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.

    • Admin
      Admin over 11 years
      See stackoverflow.com/q/13054057/395760 for the distinction between iterators and things which you can iterate over in a for loop.
    • Jeff
      Jeff over 11 years
      Would it be correct to say that generators are iterables, but not iterators?
    • Oleh Prypin
      Oleh Prypin over 11 years
      @Jeff Iterables are objects that iter can be used on to obtain an iterator. Iterators are objects that can be iterated through using next. Generators is a category of iterators (generator functions and generator expressions). At least that's what I think...
  • kindall
    kindall over 11 years
    Another nice feature of range objects is that they have a __contains__ method which can be used to test whether a value is in a range: 5 in range(10) => True
  • Jeff
    Jeff over 11 years
    Thanks for the answer; this makes sense now. The only thing I want to clear up before accepting your answer is the note in italics about a third of the way down this page, that states that "in Python 3 range() is a generator". Is this simply incorrect?
  • Oleh Prypin
    Oleh Prypin over 11 years
    @Jeff Strictly speaking, yes, it is wrong. The author of the note probably meant that in Python 3 range is lazy (compared to Python 2 where it's just a function that returns a list).
  • Lennart Regebro
    Lennart Regebro over 11 years
    Also: range(0,10,3)[3] and 9 in range(0,10,3). Range is pretty much a lazy list.
  • temporary_user_name
    temporary_user_name about 10 years
    One of the final puzzle pieces falls into place...great answer.
  • user2399453
    user2399453 over 6 years
    What is the link between being immutable and not being directly iterable? Even an iterable object seems immutable to me so what exactly is the benefit of or reason behind a range object not being directly iterable?
  • Oleh Prypin
    Oleh Prypin over 6 years
    @user3079275 "directly iterable" is a misnomer, actually meaning "iterator". Iterators have internal state and so are mutable by definition. "Iterable" is an object, whether it is mutable or not, that can produce an iterator. Even mutable objects aren't usually iterators themselves, instead they produce iterators in a reusable manner (for example, you can iterate over the same list in two different places independently, using two iterators).