Ответ 1
range
возвращает итеративный, а не итератор. Он может создавать итераторы, когда необходима итерация. Это не генератор.
Выражение генератора оценивает итератор (и, следовательно, итерабель).
Я написал это и ожидал 0
:
>>> x = range(20)
>>> next(x)
Вместо этого я получил:
TypeError: объект 'range' не является итератором
Но я думал, что это генератор?
Первоначальный ответ дал то же самое, что я изначально сказал себе: это итерируемый, а не интрактор. Но тогда это не объяснит, почему это работает, если оба являются просто генераторами:
>>> x = (i for i in range(30))
>>> next(x)
0
range
возвращает итеративный, а не итератор. Он может создавать итераторы, когда необходима итерация. Это не генератор.
Выражение генератора оценивает итератор (и, следовательно, итерабель).
Объект 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.
Встроенный next
вызывает метод hook __next__
. Итак, объекты range
имеют четко определенный __iter__
, но не четко определенный __next__
.
Итерируемые объекты имеют __iter__
, объекты-итераторы имеют четко определенные __next__
(обычно с методом __iter__
, который просто возвращает self
).
Это потому, что функция 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