У меня есть объект генератора, возвращаемый с помощью метода yield. Подготовка к вызову этого генератора довольно трудоемкая операция. Вот почему я хочу использовать генератор несколько раз.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
Конечно, я имею в виду копирование контента в простой список. Есть ли способ перезагрузить мой генератор?
y = list(y)
остальную часть кода без изменений.Генераторы не могут быть перемотаны. У вас есть следующие варианты:
Запустите функцию генератора снова, перезапустив генерацию:
Сохраните результаты генератора в структуру данных в памяти или на диске, которую вы можете повторить снова:
Недостатком варианта 1 является то, что он снова вычисляет значения. Если это сильно загружает процессор, вы в итоге рассчитываете дважды. С другой стороны, обратной стороной 2 является хранилище. Весь список значений будет храниться в памяти. Если значений слишком много, это может быть непрактично.
Таким образом, у вас есть классический компромисс между памятью и обработкой . Я не могу представить способ перемотки генератора без сохранения значений или их повторного вычисления.
источник
источник
Вероятно, самое простое решение - обернуть дорогую деталь в объект и передать ее генератору:
Таким образом, вы можете кэшировать дорогие вычисления.
Если вы можете хранить все результаты в ОЗУ одновременно, используйте их
list()
для материализации результатов генератора в виде простого списка и работы с ним.источник
Я хочу предложить другое решение старой проблемы
Преимущество этого по сравнению с чем-то вроде того
list(iterator)
, что этоO(1)
космическая сложность иlist(iterator)
естьO(n)
. Недостатком является то, что, если у вас есть доступ только к итератору, но не к функции, которая создала итератор, вы не можете использовать этот метод. Например, может показаться разумным сделать следующее, но это не сработает.источник
Если ответа GrzegorzOledzki не будет достаточно, вы, вероятно, можете использовать
send()
для достижения своей цели. См. PEP-0342 для более подробной информации о расширенных генераторах и выражениях yield.ОБНОВЛЕНИЕ: Также см
itertools.tee()
. Он включает в себя часть упомянутого выше компромисса между памятью и обработкой, но это может сэкономить некоторую память по сравнению с простым хранением генератора, что приводит кlist
; это зависит от того, как вы используете генератор.источник
Если ваш генератор является чистым в том смысле, что его вывод зависит только от переданных аргументов и номера шага, и вы хотите, чтобы полученный генератор был перезапускаемым, вот фрагмент сортировки, который может быть полезен:
выходы:
источник
Из официальной документации тройника :
Так что лучше использовать
list(iterable)
вместо этого в вашем случае.источник
list()
помещает все повторяемое в памятьtee()
если один итератор использует все значения - вот как этоtee
работает.Использование функции-оболочки для обработки
StopIteration
Вы можете написать простую функцию-обертку для вашей функции генератора, которая отслеживает, когда генератор исчерпан. Это будет сделано с использованием
StopIteration
исключения, которое генерирует генератор, когда достигает конца итерации.Как вы можете заметить выше, когда наша функция-обертка ловит
StopIteration
исключение, она просто повторно инициализирует объект генератора (используя другой экземпляр вызова функции).И затем, предполагая, что вы определяете свою функцию генерации генератора где-то, как показано ниже, вы можете использовать синтаксис декоратора функции Python для ее неявного переноса:
источник
Вы можете определить функцию, которая возвращает ваш генератор
Теперь вы можете делать столько раз, сколько захотите:
источник
Я не уверен, что вы имели в виду под дорогостоящим препаратом, но я думаю, у вас действительно есть
Если это так, почему бы не использовать повторно
data
?источник
Там нет опции для сброса итераторов. Итератор обычно выскакивает, когда он перебирает
next()
функцию. Единственный способ - сделать резервную копию перед итерацией на объекте итератора. Проверьте ниже.Создание объекта итератора с элементами от 0 до 9
Итерация по функции next (), которая появится
Преобразование объекта итератора в список
поэтому пункт 0 уже выпал. Также все элементы появляются, когда мы конвертируем итератор в список.
Поэтому перед началом итерации необходимо преобразовать итератор в списки для резервного копирования. Список может быть преобразован в итератор с
iter(<list-object>)
источник
Теперь вы можете использовать
more_itertools.seekable
(сторонний инструмент), который позволяет сбросить итераторы.Установить через
> pip install more_itertools
Примечание: потребление памяти увеличивается при продвижении итератора, так что будьте осторожны с большими итерациями.
источник
Вы можете сделать это, используя itertools.cycle (), вы можете создать итератор с помощью этого метода, а затем выполнить цикл for для итератора, который зациклит его значения.
Например:
сгенерирует 20 чисел, от 0 до 4 раз.
Примечание от документов:
источник
Хорошо, вы говорите, что хотите вызывать генератор несколько раз, но инициализация стоит дорого ... Как насчет этого?
В качестве альтернативы, вы можете просто создать свой собственный класс, который следует протоколу итератора и определяет какую-то функцию «сброса».
https://docs.python.org/2/library/stdtypes.html#iterator-types http://anandology.com/python-practice-book/iterators.html
источник
__call__
Мой ответ решает немного другую проблему: если генератор дорог для инициализации, а каждый сгенерированный объект дорог для генерации. Но нам нужно использовать генератор несколько раз в нескольких функциях. Чтобы вызвать генератор и каждый сгенерированный объект ровно один раз, мы можем использовать потоки и запускать каждый из потребляющих методов в разных потоках. Мы можем не достичь истинного параллелизма благодаря GIL, но мы достигнем нашей цели.
Этот подход хорошо зарекомендовал себя в следующем случае: модель глубокого обучения обрабатывает много изображений. В результате получается множество масок для множества объектов на изображении. Каждая маска потребляет память. У нас есть около 10 методов, которые делают разные статистические данные и метрики, но они берут все изображения одновременно. Все изображения не могут поместиться в памяти. Моеходы могут быть легко переписаны, чтобы принять итератор.
Ussage:
источник
itertools.islice
или для асинхронногоaiostream.stream.take
, и этот пост позволяет вам сделать это asyn / await способом stackoverflow.com/a/42379188/149818Это может быть сделано с помощью объекта кода. Вот пример.
1 2 3 4
1 2 3 4
источник
exec
то, что немного не рекомендуется для такого простого случая.