Итератор похож на поток предметов. Вы можете смотреть только элементы в потоке по одному, и вы только когда-либо имеете доступ к первому элементу. Чтобы посмотреть что-то в потоке, вам нужно удалить его из потока, и как только вы возьмете что-то из верхней части потока, он исчез из потока навсегда.
Когда вы позвоните по номеру zip(i, i)
, zip
сначала смотрит на первый поток и берет товар. Затем он смотрит на второй поток (который, случается, является тем же потоком как первый) и берет элемент. Затем он создает кортеж из этих двух предметов и повторяет это снова и снова, пока в потоке ничего не осталось.
Возможно, будет легче узнать, должен ли я писать функцию zip
в чистом питоне (для простоты всего 2 аргумента). Это будет выглядеть примерно так :
def zip(a, b):
out = []
try:
while True:
item1 = next(a)
item2 = next(b)
out.append((item1, item2))
except StopIteration:
return out
Теперь представьте себе случай, когда вы говорите о том, где a
и b
является тем же объектом. В этом случае мы просто вызываем next
дважды на итераторе (i
в вашем примере), который будет просто взять первые два элемента из i
в последовательности и упаковать их в кортеж.
Как только мы поняли, почему zip(i, i)
ведет себя так, как это делается, zip(*([i] * 2))
не слишком сложно. Позволяет читать выражение изнутри ...
[i] * 2
Это просто создает новый список (длины 2), где оба элемента являются ссылками на итератор i
. Так что это то же самое, что и zip(*[i, i])
(удобнее писать, когда вы хотите повторить что-то много больше, чем 2 раза). *
распаковка - распространенная идиома в python, и вы можете найти более подробную информацию в the python tutorial.Суть его в том, что python принимает итеративный и «распаковывает» его, как если бы каждый элемент итерабельного был отдельным позиционным аргументом функции. Итак:
zip(*[i, i])
делает то же самое, как:
zip(i, i)
И теперь Боба наш дядя. Мы только что приехали на полный круг, так как zip(i, i)
- это начало этой дискуссии.
Этот примерный код определенно упрощен больше, чем только упомянутый, только принимающий 2 аргумента. Например, zip
, вероятно, будет вызывать iter
на входные аргументы, так что он работает для любых итерацию (не только итераторы), но это должно быть достаточно, чтобы получить точку через ...
'[я] * 2 == [i, i] '/' func (* [i, i]) == func (i, i) ' – falsetru