Почему объект диапазона "не является итератором"?

Я написал это и ожидал 0:

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

Вместо этого я получил:

TypeError: объект 'range' не является итератором

Но я думал, что это генератор?

Первоначальный ответ дал то же самое, что я изначально сказал себе: это итерируемый, а не интрактор. Но тогда это не объяснит, почему это работает, если оба являются просто генераторами:

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

Ответы

Ответ 1

range возвращает итеративный, а не итератор. Он может создавать итераторы, когда необходима итерация. Это не генератор.

Выражение генератора оценивает итератор (и, следовательно, итерабель).

Ответ 2

Объект range iserable. Однако это не итератор.

Чтобы получить итератор, сначала необходимо вызвать iter():

>>> 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>

Изменить: но будьте осторожны, чтобы не звонить iter() при каждом вызове next(). Он создает новый итератор с индексом 0.

Ответ 3

Встроенный next вызывает метод hook __next__. Итак, объекты range имеют четко определенный __iter__, но не четко определенный __next__.

Итерируемые объекты имеют __iter__, объекты-итераторы имеют четко определенные __next__ (обычно с методом __iter__, который просто возвращает self).

Ответ 4

Это потому, что функция next вызывает метод next объекта, который прошел.

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

listiterator и generator оба имеют метод next.

>>> 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>

Но a list не имеет этого. И именно по этой причине он вызывает это исключение.

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

next не заботится о том, является ли объект, который он передал, итератором или нет.

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

Но добавление метода next не обязательно делает его сборником/последовательностью/итерируемым.

>>> 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

Но добавление к нему метода __iter__ делает его одним.

>>> 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>

Кажется, что next имеет встроенный интеллект, когда дело доходит до 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