Python + Iterator в результате обмена текущим состоянием карты с исходным итератором

Позвольте мне проиллюстрировать это примером, с которым мы столкнулись с моими учениками:

>>>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 использует за сценой?

map() оценивает лениво. Поэтому каждый раз, когда вы оцениваете следующий элемент, он вызывает next() на a_lot. В результате вызов next(twice_a_lot) вызывает next(a_lot), чтобы получить следующее значение для передачи его функции. В общем случае итераторы могут создавать заданную последовательность только один раз. Вы можете раздавать их, но это все равно что делить дозатор pez — вы не можете есть одну и ту же конфету и делиться ею.
Mark 23.12.2020 23:13

Вы используете один и тот же генератор a_lot в обоих, конечно, он общий.

zvone 23.12.2020 23:13

Кстати, если бы map не был ленивым, то он бы потреблял все a_lot и next(a_lot) потерпел бы неудачу.

zvone 23.12.2020 23:14

Это вовсе не сумасшествие. Как еще это может работать? Когда вы вызываете iter итератор, он возвращает себя.

juanpa.arrivillaga 23.12.2020 23:16

поэтому я могу получить итератор2, вычисленный из итератора1 () через карту (...), НО независимый от того, как я прохожу их с помощью следующего... Должен ли я что-то .copy()? Хорошо, я понял, начните заново с первоначальной формулы, не сочиняйте...

smed 23.12.2020 23:17

@juanpa.arrivillaga Я никогда не говорил, что это сумасшествие, я просто был удивлен, когда впервые увидел это...

smed 23.12.2020 23:18

Кстати, range(10e50) недействителен, так как range не может принимать float аргумент.

chepner 23.12.2020 23:29

хорошо, я должен тебе range(int(10e50)). Первоначальная идея была range(10000000000000000000000000000000000000000000000000000)‌​, типа не надо list(map(lambda x:2*x, a_lot)), нет не надо, ты не хочешь этого...

smed 23.12.2020 23:33
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
8
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Сначала это может показаться удивительным, но после небольшого размышления должно показаться очевидным.

Когда вы создаете итератор из другого итератора, невозможно восстановить исходное состояние любого базового контейнера, над которым вы выполняете итерацию (в данном случае, объекта 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). Недостатком является то, что значения, извлеченные из одного итератора, необходимо кэшировать, пока другой итератор не догонит их.

chepner 23.12.2020 23:35

Другие вопросы по теме