Why is the range object “not an iterator”? [duplicate]

妖精的绣舞 提交于 2019-12-20 17:27:24

问题


I wrote this and expected 0:

>>> x = range(20)
>>> next(x)

Instead I got:

TypeError: 'range' object is not an iterator

But I thought it was a generator?

The initial answer yielded the same thing I initially said to myself: it's an iterable, not an interator. But then, that wouldn't explain why this works, if both are simply generators:

>>> x = (i for i in range(30))
>>> next(x)
0

回答1:


range returns an iterable, not an iterator. It can make iterators when iteration is necessary. It is not a generator.

A generator expression evaluates to an iterator (and hence an iterable as well).




回答2:


The range object is iterable. However, it's not an iterator.

To get an iterator, you need to call iter() first:

>>> r=range(5,15)
>>> next(iter(r))
5
>>> next(iter(r))
5
>>> next(iter(r))
5
>>> next(iter(r))
5
>>> i=iter(r)
>>> next(i)
5
>>> next(i)
6
>>> next(i)
7
>>> next(i)
8
>>> iter(r)
<range_iterator object at 0x10b0f0630>
>>> iter(r)
<range_iterator object at 0x10b0f0750>
>>> iter(r)
<range_iterator object at 0x10b0f0c30>

Edit: But be careful not to call iter() with every call to next(). It creates a new iterator at index 0.




回答3:


The next builtin calls the __next__ hook method. So, range objects have a well defined __iter__, but not a well-defined __next__.

iterable objects have __iter__ defined, iterator objects have well defined __next__ (typically with an __iter__ method which simply returns self).




回答4:


It's because the next function calls the next method of the object that passed in.

next(...)
    x.next() -> the next value, or raise StopIteration

listiterators and generators both have the next method.

>>> iter(range(1)).__class__.next
<slot wrapper 'next' of 'listiterator' objects>
>>> iter(x for x in range(1)).__class__.next
<slot wrapper 'next' of 'generator' objects>

But a list doesn't have it. And that is the reason why it raises that exception.

>>> list.next
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'list' has no attribute 'next'

next doesn't care much about whether the object it's passed is an iterator or not.

>>> class Foo():
...     def next(self):
...             return "foo"
... 
>>> foo = Foo()
>>> next(foo)
'foo'
>>> next(foo)
'foo'

But adding the next method doesn't necessarily make it a collection/sequence/iterable.

>>> class Foo():
...     def next(self):
...             return "Foo"
>>> [x for x in Foo()]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iteration over non-sequence
>>> iter(Foo())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iteration over non-sequence

But adding the __iter__ method to it makes it one.

>>> class Foo():
...     def next(self):
...             return "Foo"
...     def __iter__(self): return self
... 
>>> [x for x in Foo()]
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt
>>> iter(Foo())
<__main__.Foo instance at 0x7fd77307c488>

The next seems to have some builtin intelligence when it comes to list.

>>> class Foo():
...     pass
... 
>>> foo = Foo()
>>> next(foo)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: instance has no next() method
>>> next(range(20))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list object is not an iterator


来源:https://stackoverflow.com/questions/21803830/why-is-the-range-object-not-an-iterator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!