iter()
ничего не делает для списка; list
объект имеет метод __iter__
, который iter()
использует для создания объекта итератора. Этот объект имеет ссылку на исходный список и индекс; каждый раз, когда вы запрашиваете следующее значение в итераторе, значение текущего индекса извлекается и возвращается, и индекс увеличивается.
Вы можете использовать функцию next()
, чтобы получить следующее значение от итератора:
>>> a = ['animal', 'dog', 'car', 'bmw', 'color', 'blue']
>>> a_iter = iter(a)
>>> next(a_iter) # get the next value
'animal'
>>> next(a_iter) # get the next value
'dog'
Обратите внимание, что вызов next()
снова дает вам новое значение. Вы можете сделать это, пока не закончится итератор:
>>> three_more = next(a_iter), next(a_iter), next(a_iter)
>>> next(a_iter) # last one
'blue'
>>> next(a_iter) # nothing left
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Объекты итератора списка сохраняют исходный объект списка; изменение объекта списка отразится в значениях итератора, полученных в next()
:
>>> b = ['foo', 'bar']
>>> b_iter = iter(b)
>>> next(b_iter)
'foo'
>>> b[1] = 'spam'
>>> b
['foo', 'spam']
>>> next(b_iter)
'spam'
zip()
запрашивает следующее значение в каждом из своих аргументов, которое предполагается равным iterables ; zip()
звонит iter()
на них всех. Для объектов итераторов, таких как a_iter
, iter(a_iter)
возвращает сам итератор (в конце концов, это уже итератор):
>>> iter(a_iter)
<list_iterator object at 0x10e7b6a20>
>>> iter(a_iter) is a_iter
True
Поскольку a_iter
будет выдавать значения из исходного списка, по порядку, это означает, что вы получаете парные элементы в словаре, потому что zip()
имеет две ссылки на один и тот же объект ; вы фактически создаете (next(a_iter), next(a_iter))
в качестве значения шага итератора для zip()
. Если вы передадите две ссылки на a
, с другой стороны, zip()
вызовет iter()
дважды , создав два отдельных объекта итератора , каждый из которых имеет свой собственный индекс отслеживать.
Давайте посмотрим на это подробно. Обратите внимание, что zip()
также создает объект итератора, поэтому мы можем убедиться, что вызов next()
для zip()
, в свою очередь, приводит к тому, что a_iter
делает двойной шаг вперед:
>>> a_iter = iter(a)
>>> a_iter_zip = zip(a_iter, a_iter)
>>> a_iter_zip # a zip object is an iterator too
<zip object at 0x10e7ba8c8>
>>> next(a_iter_zip) # get next value of a_iter, together with the next value of a_iter
('animal', 'dog')
>>> next(a_iter) # the a-list iterator was advanced, so now we get 'car'
'car'
>>> next(a_iter_zip) # now a_iter is at bmw, so we get bmw and color
('bmw', 'color')
Итераторы являются независимыми объектами, каждый из которых имеет свой собственный индекс:
>>> a_iter1 = iter(a)
>>> a_iter2 = iter(a) # different iterator from a_iter1
>>> next(a_iter1), next(a_iter1) # what zip() does
('animal', 'dog')
>>> next(a_iter2), next(a_iter2) # iter2 is independent
('animal', 'dog')
Таким образом, когда вы используете zip(a, a)
, на самом деле происходит то, что zip()
вызывает iter(a)
два раза, создавая два новых итератора, и оба используются для создания вывода:
>>> a_iter1 = iter(a)
>>> a_iter2 = iter(a)
>>> a_iter_1_and_2_zip = zip(a_iter1, a_iter2)
>>> next(a_iter_1_and_2_zip) # values from a_iter1 and a_iter2
('animal', 'animal')
>>> next(a_iter_1_and_2_zip) # moving in lockstep
('dog', 'dog')
>>> next(a_iter1) # moving one of these two one step along, to 'car'
'car'
>>> next(a_iter_1_and_2_zip) # so a_iter1 is one step ahead!
('bmw', 'car')
>>> next(a_iter1) # another extra step
'color'
>>> next(a_iter_1_and_2_zip) # so a_iter1 is two steps ahead!
('blue', 'bmw')