Позвольте мне проиллюстрировать это примером, с которым мы столкнулись с моими учениками:
>>>a_lot = (i for i in range(10e50))
>>>twice_a_lot = map(lambda x: 2*x, a_lot)
>>>next(a_lot)
0
>>>next(a_lot)
1
>>>next(a_lot)
2
>>>next(twice_a_lot)
6
Так что каким-то образом эти итераторы делятся своим текущим состоянием, как бы безумно и неудобно это ни звучало... Любые намеки на модель, которую Python использует за сценой?
Вы используете один и тот же генератор a_lot
в обоих, конечно, он общий.
Кстати, если бы map
не был ленивым, то он бы потреблял все a_lot
и next(a_lot)
потерпел бы неудачу.
Это вовсе не сумасшествие. Как еще это может работать? Когда вы вызываете iter
итератор, он возвращает себя.
поэтому я могу получить итератор2, вычисленный из итератора1 () через карту (...), НО независимый от того, как я прохожу их с помощью следующего... Должен ли я что-то .copy()? Хорошо, я понял, начните заново с первоначальной формулы, не сочиняйте...
@juanpa.arrivillaga Я никогда не говорил, что это сумасшествие, я просто был удивлен, когда впервые увидел это...
Кстати, range(10e50)
недействителен, так как range
не может принимать float
аргумент.
хорошо, я должен тебе range(int(10e50))
. Первоначальная идея была range(10000000000000000000000000000000000000000000000000000)
, типа не надо list(map(lambda x:2*x, a_lot))
, нет не надо, ты не хочешь этого...
Сначала это может показаться удивительным, но после небольшого размышления должно показаться очевидным.
Когда вы создаете итератор из другого итератора, невозможно восстановить исходное состояние любого базового контейнера, над которым вы выполняете итерацию (в данном случае, объекта range
). По крайней мере, не в целом.
Рассмотрим простейший случай: iter(something)
.
Когда something
является итератором, то согласно спецификации протокола итератора , iterator.__iter__
должен:
Вернуть сам объект итератора
Другими словами, если вы правильно реализовали протокол, то всегда будет сохраняться следующая идентичность:
iter(iterator) is iterator
Конечно, у map
может быть какое-то соглашение, позволяющее восстановить и создать независимый итератор, но такого соглашения нет. В общем, если вы хотите создать независимые итераторы, вам нужно создать их из исходного кода.
И, конечно же, есть итераторы, в которых это действительно невозможно без сохранения всех предыдущих результатов. Учитывать:
import random
def random_iterator():
while True:
yield random.random()
В таком случае, как должна работать карта со следующим?
iterator = random_iterator()
twice = map(lambda x: x*2, iterator)
Хорошо, спасибо за все полученные комментарии (менее чем за 5 минут!!!!). Я понял связанные вещи: если мне нужны два независимых итератора, я не буду использовать карту для вычисления snd из fst:
>>>a_lot = (i for i in range(10e50))
>>>twice_a_lot = (2*i for i in range(10e50))
и я буду помнить, что map
ленив, потому что нет другого способа, который мог бы иметь смысл.
Это был хороший SO
урок.
СПАСИБО
Вы также можете использовать itertools.tee
, если по какой-либо причине вы не можете жестко закодировать определение итерации дважды: a_lot, tmp = tee(range(10**50)); twice_a_lot = (2*i for i in tmp)
. Недостатком является то, что значения, извлеченные из одного итератора, необходимо кэшировать, пока другой итератор не догонит их.
map()
оценивает лениво. Поэтому каждый раз, когда вы оцениваете следующий элемент, он вызываетnext()
наa_lot
. В результате вызовnext(twice_a_lot)
вызываетnext(a_lot)
, чтобы получить следующее значение для передачи его функции. В общем случае итераторы могут создавать заданную последовательность только один раз. Вы можете раздавать их, но это все равно что делить дозатор pez — вы не можете есть одну и ту же конфету и делиться ею.