Что такое код yield


Содержание

Какое ключевое слово yield используется для С#?

В вопросе Как я могу раскрыть только фрагмент IList<> у одного из ответов был следующий фрагмент кода:

Что делает здесь ключевое слово yield? Я видел ссылки в нескольких местах, и еще один вопрос, но я не совсем понял, что он на самом деле делает. Я привык думать о доходности в том смысле, что один поток уступает другому, но здесь это не актуально.

Ключевое слово yield самом деле очень много здесь делает.

Функция возвращает объект, который реализует интерфейс IEnumerable . Если вызывающая функция начинает foreach по этому объекту, функция вызывается снова, пока она не «выдаст». Это синтаксический сахар, введенный в С# 2.0. В более ранних версиях вы должны были создавать свои собственные объекты IEnumerable и IEnumerator чтобы делать подобные вещи.

Самый простой способ понять такой код — набрать пример, установить несколько точек останова и посмотреть, что произойдет. Попробуйте пройти этот пример:

Проходя по примеру, вы обнаружите, что первый вызов Integers() возвращает 1 . Второй вызов возвращает 2 а строка yield return 1 больше не выполняется.

Вот реальный пример:

итерация. Он создает конечный автомат «под крышками», который помнит, где вы были на каждом дополнительном цикле функции и выбирает оттуда.

Выход имеет два больших использования,

Он помогает обеспечить пользовательскую итерацию без создания коллекций temp.

Это помогает делать итерацию с сохранением состояния.

Чтобы объяснить выше два пункта более демонстративно, я создал простое видео, которое вы можете посмотреть здесь

Недавно Раймонд Чен также опубликовал интересную серию статей по ключевому слову yield.

Хотя он номинально используется для простой реализации шаблона итератора, но может быть обобщен в конечный автомат. Нет смысла цитировать Рэймонда, последняя часть также ссылается на другое использование (но пример в блоге Entin особенно хорош, показывая, как писать асинхронный безопасный код).

На первый взгляд возвращаемая доходность — это сахар .NET, возвращающий IEnumerable.

Без выхода все элементы коллекции создаются сразу:

Тот же код с помощью yield, он возвращает элемент за элементом:

Преимущество использования yield заключается в том, что если функция, потребляющая ваши данные, просто нуждается в первом элементе коллекции, остальные элементы не будут созданы.

Оператор yield позволяет создавать элементы по мере необходимости. Это хорошая причина, чтобы использовать его.

yield return используется с счетчиками. При каждом вызове оператора yield управление возвращается вызывающему, но оно обеспечивает сохранение состояния вызываемого абонента. В связи с этим, когда вызывающий абонент перечисляет следующий элемент, он продолжает выполнение в методе callee из инструкции сразу после оператора yield .

Попробуем понять это на примере. В этом примере, соответствующем каждой строке, я упомянул порядок, в котором выполняется поток.

Кроме того, состояние поддерживается для каждого перечисления. Предположим, у меня есть другой вызов метода Fibs() , тогда для него будет reset.

Интуитивно ключевое слово возвращает значение из функции, не оставляя его, то есть в вашем примере кода оно возвращает текущее значение item , а затем возобновляет цикл. Более формально он используется компилятором для генерации кода для итератора. Итераторы — это функции, возвращающие объекты IEnumerable . MSDN содержит несколько статей о них.

Реализация списка или массива сразу же загружает все элементы, тогда как реализация yield предоставляет отложенное решение для выполнения.

На практике часто желательно выполнять минимальный объем работы по мере необходимости, чтобы уменьшить потребление ресурсов приложением.

Например, у нас может быть приложение, которое обрабатывает миллионы записей из базы данных. Следующие преимущества могут быть достигнуты, когда мы используем IEnumerable в отложенной модели pull-based исполнения:

  • Масштабируемость, надежность и предсказуемость, вероятно, улучшатся, поскольку количество записей существенно не влияет на требования к ресурсам приложений.
  • Эффективность и отзывчивость, вероятно, улучшатся, так как обработка может начаться немедленно, а не ждать загрузки первой коллекции.
  • Возможность восстановления и использования, вероятно, улучшится, так как приложение может быть остановлено, запущено, прервано или не выполнено. Только те элементы, которые будут выполняться, будут потеряны по сравнению с предварительной выборкой всех данных, в которых фактически использовалась только часть результатов.
  • Непрерывная обработка возможна в средах, где добавляются потоки постоянной рабочей нагрузки.

Вот сравнение между сборкой первой, такой как список, по сравнению с использованием yield.

Пример списка

Консольный выход

ContactListStore: Создание контакта 1

ContactListStore: создание контакта 2

ContactListStore: создание контакта 3

Готовы к итерации через коллекцию.

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

Пример дохода

Консольный выход

Готовы к итерации через коллекцию.

Примечание. Коллекция не была выполнена вообще. Это связано с характером «отсроченного исполнения» IEnumerable. Построение элемента будет происходить только тогда, когда это действительно необходимо.

Позвольте снова вызвать коллекцию и перенести поведение, когда мы получаем первый контакт в коллекции.

Консольный выход

Готов к итерации по коллекции

ContactYieldStore: создание контакта 1

Ницца! Только первый контакт был создан, когда клиент «вытащил» элемент из коллекции.

Вот простой способ понять концепцию: Основная идея состоит в том, что если вы хотите использовать коллекцию, в которой вы можете использовать » foreach «, но сбор элементов в коллекции по какой-либо причине дорогостоящий (например, запрос из базы данных), и вам часто не понадобится целую коллекцию, то вы создаете функцию, которая собирает коллекцию по одному элементу за раз и возвращает ее обратно потребителю (кто может рано разорвать процесс сбора).

Подумайте об этом так: Вы идете на счетчик мяса и хотите купить фунт нарезанной ветчины. Мясник берет 10-фунтовую ветчину на спину, кладет ее на машину для резки, нарезает все это, затем возвращает кучу ломтиков вам и измеряет фунт. (OLD). С помощью yield мясник выводит машину сглаживателя на счетчик и начинает нарезать и «уронить» каждый срез на шкале до тех пор, пока он не будет измерять 1 фунт, а затем обертывает его для вас, и все готово. Старый путь может быть лучше для мясника (позволяет ему организовывать свою технику так, как ему нравится), но в большинстве случаев новый способ явно более эффективен для потребителя.

Ключевое слово yield позволяет создать IEnumerable в форме в блоке итератора. Этот блок итератора поддерживает отложенное выполнение, и если вы не знакомы с концепцией, он может показаться почти волшебным. Однако, в конце концов, это просто код, который выполняется без каких-либо странных уловок.

Блок итератора может быть описан как синтаксический сахар, где компилятор генерирует конечный автомат, который отслеживает, как далеко продвинулось перечисление перечисляемого. Чтобы перечислить перечислимое, вы часто используете цикл foreach . Однако цикл foreach также является синтаксическим сахаром. Таким образом, вы удалили две абстракции из реального кода, поэтому изначально может быть трудно понять, как все это работает вместе.

Предположим, что у вас есть очень простой блок итератора:

Реальные блоки итераторов часто имеют условия и циклы, но когда вы проверяете условия и разворачиваете циклы, они все равно заканчиваются тем, что операторы yield чередуются с другим кодом.

Для перечисления блока итератора используется цикл foreach :

Вот результат (здесь нет сюрпризов):

Как указано выше, foreach является синтаксическим сахаром:

В попытке распутать это я создал диаграмму последовательности с удаленными абстракциями:

Конечный автомат, сгенерированный компилятором, также реализует перечислитель, но чтобы сделать диаграмму более понятной, я показал их как отдельные экземпляры. (Когда конечный автомат перечисляется из другого потока, вы фактически получаете отдельные экземпляры, но эта деталь здесь не важна.)

Каждый раз, когда вы вызываете свой блок итератора, создается новый экземпляр конечного автомата. Однако ни один из вашего кода в блоке итератора не будет выполнен, пока enumerator.MoveNext() будет выполнен в первый раз. Вот как работает отложенное выполнение. Вот (довольно глупый) пример:

На данный момент итератор не выполнен. Предложение Where создает новый IEnumerable который оборачивает IEnumerable возвращаемый IteratorBlock но этот перечислимый еще предстоит перечислить. Это происходит, когда вы выполняете цикл foreach :

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

Обратите внимание, что методы LINQ, такие как ToList() , ToArray() , First() , Count() и т.д., Будут использовать цикл foreach для перечисления перечислимого. Например, ToList() перечислит все элементы перечисляемого и сохранит их в списке. Теперь вы можете получить доступ к списку, чтобы получить все элементы перечислимого без повторного выполнения блока итератора. Существует компромисс между использованием ЦП для создания элементов перечислимого несколько раз и памяти для хранения элементов перечисления для многократного доступа к ним при использовании таких методов, как ToList() .

Если я правильно понимаю, здесь, как я бы сформулировал это с точки зрения функции, реализующей IEnumerable с yield.

  • Здесь один.
  • Звоните еще раз, если вам нужен другой.
  • Я буду помнить то, что я тебе уже дал.
  • Я буду знать только, смогу ли я дать вам еще один, когда вы снова позвоните.

Ключевое слово С# yield, просто говоря, допускает много вызовов к телу кода, называемому итератором, который знает, как вернуться до его выполнения и, когда вызывается снова, продолжается там, где он был остановлен, т.е. помогает итератору стать прозрачным с точки зрения состояния для каждого элемента в последовательности, в которой итератор возвращается при последовательных вызовах.

В JavaScript эта же концепция называется генераторами.

Это простой и простой способ создания перечислимого для вашего объекта. Компилятор создает класс, который обертывает ваш метод и который реализует в этом случае IEnumerable . Без ключевого слова yield вы должны создать объект, который реализует IEnumerable .


Он производит перечислимую последовательность. То, что он делает, фактически создает локальную последовательность IEnumerable и возвращает ее как результат метода

Как только вы хорошо понимаете, как работают итераторные блоки, У Эрика Липперта отличная серия сообщений в блоге на некоторых, казалось бы, нечетных ограничения на общность блоков итератора.

Здесь даже более простые примеры

Обратите внимание, что возврат результата не возвращается из метода. Вы даже можете поставить WriteLine после yield return

Вышеизложенное генерирует IEnumerable из 4 целых чисел 4,4,4,4

Здесь с WriteLine . Будет добавлен 4 в список, напечатайте abc, затем добавьте 4 в список, затем заполните метод и верните его обратно из метода (как только метод завершится, как это происходит с процедурой без возврата). Но это будет иметь значение, IEnumerable список int s, который он возвращает по завершении.

Обратите внимание также, что при использовании yield вы возвращаете не тот же тип, что и функция. Это тип элемента в списке IEnumerable .

Вы используете yield с возвращаемым типом метода как IEnumerable . Если тип возвращаемого метода — int или List , и вы используете yield , то он не будет компилироваться. Вы можете использовать тип возвращаемого метода IEnumerable без урока, но, похоже, вы не можете использовать выход без IEnumerable метода return type.

И чтобы заставить его выполнить, вы должны вызвать его особым образом.

Чтобы понять, что дает урожай, вы должны понять, что такое генераторы. И прежде чем вы сможете понять генераторы, вы должны понять итерируемые.

Итерации Когда вы создаете список, вы можете читать его элементы по одному. Чтение его элементов по одному называется итерацией:

Mylist является повторяемым. Когда вы используете понимание списка, вы создаете список, и поэтому повторяемый:

Все, что вы можете использовать «для. в. «, является итеративным; списки, строки, файлы.

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

Генераторы Генераторы — это итераторы, вид итераций, которые вы можете повторять только один раз. Генераторы не хранят все значения в памяти, они генерируют значения на лету:

Это то же самое, за исключением того, что вы использовали() вместо []. НО, вы не можете выполнить для меня в mygenerator второй раз, так как генераторы могут использоваться только один раз: они вычисляют 0, затем забывают об этом и вычисляют 1, и заканчивают вычислять 4, один за другим.

Yield yield — это ключевое слово, которое используется как return, за исключением того, что функция возвращает генератор.

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

Чтобы справиться с yield, вы должны понимать, что при вызове функции код, написанный в теле функции, не запускается. Функция возвращает только объект генератора, это немного сложно :-)

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

Теперь самая сложная часть:

При первом вызове for вызывает объект генератора, созданный из вашей функции, он будет запускать код в вашей функции с самого начала, пока не достигнет yield, а затем вернет первое значение цикла. Затем каждый следующий вызов будет запускать цикл, который вы написали в функции, еще раз и возвращать следующее значение, пока значение не будет возвращено.

Генератор считается пустым после запуска функции, но больше не влияет на yield. Это может быть из-за того, что цикл закончился, или из-за того, что вы больше не удовлетворяете «если/еще».

Ваш код объяснил генератор:

Этот код содержит несколько умных частей:

Цикл повторяется в списке, но список расширяется во время итерации цикла :-) Это краткий способ пройти через все эти вложенные данные, даже если это немного опасно, так как вы можете получить бесконечный цикл. В этом случае candid.extend(node._get_child_candidates (distance, min_dist, max_dist)) исчерпывает все значения генератора, но при этом продолжает создавать новые объекты генератора, которые будут генерировать значения, отличные от предыдущих, поскольку он не применяется к одному и тому же узел.

Метод extend() — это метод объекта списка, который ожидает итерацию и добавляет ее значения в список.

Обычно мы передаем ему список:

Но в вашем коде он получает генератор, что хорошо, потому что:

Вам не нужно читать значения дважды. У вас может быть много детей, и вы не хотите, чтобы они все хранились в памяти. И это работает, потому что Python не заботится, является ли аргумент метода списком или нет. Python ожидает итерации, поэтому он будет работать со строками, списками, кортежами и генераторами! Это называется утка и является одной из причин, почему Python такой крутой. Но это другая история, для другого вопроса.

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

Контроль истощения генератора

Примечание: Для Python 3, useprint (. Corner_street_atm следующий()) или печать (следующая (corner_street_atm))

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

Itertools, ваш лучший друг Модуль itertools содержит специальные функции для управления итерациями. Вы когда-нибудь хотели дублировать генератор? Цепочка двух генераторов? Группировать значения во вложенном списке с одной линией? Карта /Zip без создания другого списка?

Тогда просто импортируйте itertools.

Пример? Давайте рассмотрим возможные порядки заезда на скачки:

Понимание внутренних механизмов итерации Итерация — это процесс, подразумевающий итерации (реализующие метод iter()) и итераторы (реализующие метод next()). Итерации — это любые объекты, от которых вы можете получить итератор. Итераторы — это объекты, которые позволяют повторять итерации.

Генераторы

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/generators.

Генераторы – новый вид функций в современном JavaScript. Они отличаются от обычных тем, что могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени.

Создание генератора

Для объявления генератора используется новая синтаксическая конструкция: function* (функция со звёздочкой).

Её называют «функция-генератор» (generator function).

Выглядит это так:

При запуске generateSequence() код такой функции не выполняется. Вместо этого она возвращает специальный объект, который как раз и называют «генератором».

Правильнее всего будет воспринимать генератор как «замороженный вызов функции»:

При создании генератора код находится в начале своего выполнения.

Основным методом генератора является next() . При вызове он возобновляет выполнение кода до ближайшего ключевого слова yield . По достижении yield выполнение приостанавливается, а значение – возвращается во внешний код:

Повторный вызов generator.next() возобновит выполнение и вернёт результат следующего yield :

И, наконец, последний вызов завершит выполнение функции и вернёт результат return :

Функция завершена. Внешний код должен увидеть это из свойства done:true и обработать value:3 , как окончательный результат.

Новые вызовы generator.next() больше не имеют смысла. Впрочем, если они и будут, то не вызовут ошибки, но будут возвращать один и тот же объект: .

«Открутить назад» завершившийся генератор нельзя, но можно создать новый ещё одним вызовом generateSequence() и выполнить его.

Можно ставить звёздочку как сразу после function , так и позже, перед названием. В интернете можно найти обе эти формы записи, они верны:

Технически, нет разницы, но писать то так то эдак – довольно странно, надо остановиться на чём-то одном.

Автор этого текста полагает, что правильнее использовать первый вариант function* , так как звёздочка относится к типу объявляемой сущности ( function* – «функция-генератор»), а не к её названию. Конечно, это всего лишь рекомендация-мнение, не обязательное к выполнению, работать будет в любом случае.

Генератор – итератор

Как вы, наверно, уже догадались по наличию метода next() , генератор связан с итераторами. В частности, он является итерируемым объектом.

Его можно перебирать и через for..of :

Заметим, однако, существенную особенность такого перебора!

При запуске примера выше будет выведено значение 1 , затем 2 . Значение 3 выведено не будет. Это потому что стандартный перебор итератора игнорирует value на последнем значении, при done: true . Так что результат return в цикле for..of не выводится.

Соответственно, если мы хотим, чтобы все значения возвращались при переборе через for..of , то надо возвращать их через yield :

…А зачем вообще return при таком раскладе, если его результат игнорируется? Он тоже нужен, но в других ситуациях. Перебор через for..of – в некотором смысле «исключение». Как мы увидим дальше, в других контекстах return очень даже востребован.

Композиция генераторов


Один генератор может включать в себя другие. Это называется композицией.

Разберём композицию на примере.

Пусть у нас есть функция generateSequence , которая генерирует последовательность чисел:

Мы хотим на её основе сделать другую функцию generateAlphaNum() , которая будет генерировать коды для буквенно-цифровых символов латинского алфавита:

Далее этот набор кодов можно превратить в строку и использовать, к примеру, для выбора из него случайного пароля. Только символы пунктуации ещё хорошо бы добавить для надёжности, но в этом примере мы будем без них.

Естественно, раз в нашем распоряжении есть готовый генератор generateSequence , то хорошо бы его использовать.

Конечно, можно внутри generateAlphaNum запустить несколько раз generateSequence , объединить результаты и вернуть. Так мы бы сделали с обычными функциями. Но композиция – это кое-что получше.

Она выглядит так:

Здесь использована специальная форма yield* . Она применима только к другому генератору и делегирует ему выполнение.

То есть, при yield* интерпретатор переходит внутрь генератора-аргумента, к примеру, generateSequence(48, 57) , выполняет его, и все yield , которые он делает, выходят из внешнего генератора.

Получается – как будто мы вставили код внутреннего генератора во внешний напрямую, вот так:

Код выше по поведению полностью идентичен варианту с yield* . При этом, конечно, переменные вложенного генератора не попадают во внешний, «делегирование» только выводит результаты yield во внешний поток.

Композиция – это естественное встраивание одного генератора в поток другого. При композиции значения из вложенного генератора выдаются «по мере готовности». Поэтому она будет работать даже если поток данных из вложенного генератора оказался бесконечным или ожидает какого-либо условия для завершения.

yield – дорога в обе стороны

До этого генераторы наиболее напоминали «итераторы на стероидах». Но, как мы сейчас увидим, это не так, есть фундаментальное различие, генераторы гораздо мощнее и гибче.

Всё дело в том, что yield – дорога в обе стороны: он не только возвращает результат наружу, но и может передавать значение извне в генератор.

Вызов let result = yield value делает следующее:

Оператор yield

Дата изменения: 09.10.2020

Чтобы организовать перечисляемую коллекцию для использования ее с помощью конструкции foreach или класса – перечислителя необходимо реализовать интерфейсы перечислители: IEnumerable и IEnumerator для необобщенных (неуниверсальных) коллекций, IEnumerable и IEnumerator для обобщенных (универсальных коллекций). Этот механизм можно назвать традиционным.

Однако, традиционный механизм может показаться сложным (особенно для новичков) и недостаточно емким, поскольку при реализации требует множество однотипных строк кода.

Введение ключевого слова yield внедряет в платформу .NET емкую форму реализации перечислителей. Удобство его использования в том, что yield исключает необходимость создавать отдельный тип, в котором содержится состояние перечисления. (см пример в разделе перечислителей)

Илон Маск рекомендует:  Iis использование директив включений на стороне сервера

Ключевое слово yield

Используется в операторах, методах доступа или иных методах, возвращающих перечислитель. Его использование в реализации метода означает реализацию итератора.

Существует две формы его использования:

Оператор yield return нужен для возврата элемента коллекции по одному, так как этого требует конструкция foreach. Оператор вызывается для каждого элемента, когда это происходит возвращается expression, а указатель в коллекции переводится на следующий элемент.

Вызов оператора yield break, завершает итерацию по элементам.

Ключевое слово yield нельзя использовать в анонимных методах или небезопасных участках кода (заключенных в конструкцию unsafe).

Возвращающий элемент кода с использованием yield (блок с yield return) нельзя помешать в блок try/catch, но можно и нужно в блок try/finally. Завершающий элемент кода ( блок с yield break) , напротив нельзя размещать в блоке try/finally, но можно в try/catch.

В объявление итератора, в котором будет использовано ключевое слово yield возвращаемый тип должен быть IEnumarable или IEnumerator (или их обобщенные версии). Сигнатура объявления итератора не должна использовать ссылки на значения (ref и out).

Примеры

Следующий пример демонстрирует реализацию Get-метода, который возвращает новые элементы коллекции с помощью yield return:

Ключевое слова yield и генераторы в Python

Для создания генераторов в Python используют ключевое слово yield . Генератор представляет собой коллекцию, которая производит элементы во время выполнения и может повторяться только один раз. Используя генераторы вы можете улучшить производительность своего приложения и потреблять меньше памяти в сравнении с обычными коллекциями.

В статье поясняется, как использовать и в каких случаях использовать генераторы в своем коде и то как они работают. Начнем с того что рассмотрим разницу между генератором и списком, а затем рассмотрим возможность использования yield .

Отличия генераторов и списков.

Давайте на практике разберемся в отличии списков от генераторов. Для начала создадим список: Как и ожидается тип переменной simple_list является список и перебор элементов выводит ожидаемый результат.

Давайте теперь выполним ту же операцию, но теперь используем генератор: Обратите внимание на то, что при создании генератора используются круглые скобки вместо квадратных. Переменная simple generator имеет уже другой тип, а именно generator . Как вы видите перебор элементов в цикле дает такой же результат как и в предыдущем примере и тут у вас может возникнуть вопрос: так в чем же отличия генератора от обычного списка? Одно из главных их отличий в способе хранения элементов в памяти. Списки хранят сразу все элементы в памяти, в то время как генераторы создают свои элементы на лету в процессе итерации, отображая элемент и удаляя его при переходе к следующему. Это приводит к тому, что для повторного использования генератора его необходимо инициализировать снова.

Использование ключевого слова yield .

После того как мы узнали разницу в работе генераторов и простых коллекций давайте разберем как создавать генераторы с использованием yield .

В предыдущем примере мы создали генератор неявно. Однако в более сложных случаях мы можем создавать функцию возвращающую генератор. Для того что бы обычную функцию превратить в генератор, в теле функции используется ключевое слово yield вместо return . Давайте рассмотрим на примере как это работает: В примере функция square_num использует ключевое слово yield для возврата значения квадрата числа внутри цикла for . Несмотря на то, что мы вызываем функцию square_num , она на самом деле не выполняется на данный момент времени, и в памяти еще нет вычисленных значений.

Для того что бы запустить функцию-генератор на исполнения и получить результат её работы необходимо использовать встроенный метод генератора next . Данный метод возвращает первый элемент генератора, а при последующем использовании переходит к следующему элементу. Используемый метод будет возвращать значения пока не будет достигнут последнее значение в генераторе. При достижении конца генератора метод next возвращает ошибку StopIteration :

Важно отметить, что функция-генератор square_num не хранит ни одного элемента в памяти, а значения вычисляются во время выполнения, возвращаются и удаляются из памяти. Единственной что храниться постоянно в память в таком случаи это данные состояния генератора, которые обычно намного меньше, чем большие списки.

Для получения данных из функции-генератора вместо постоянного использования метода next можно использовать цикл for для итерации по её значениям.

Оптимизация производительности.

Как уже говорилось ранее, генераторы удобны, когда речь заходит о задачах в которых обрабатывается большие объемы данных. Поскольку элементы в генераторе создаются в момент вызова и удаляются сразу после использования, объем требуемой памяти значительно сокращается.

В предыдущих примерах выигрыша от использования генератора не было, но давайте рассмотрим ситуацию в которой мы будем работать со 1000000 элементов и посмотрим как расходуется память при использовании генератора по сравнению с обычным списком.

С начала рассмотрим вариант без использования генератора. Создадим функцию которая возвращает список содержащий 1000000 элементов и вычислим используемую память до и после ее вызова. Для вычисления используемой памяти необходимо установить пакет psutil используемый для получения информации о запущенных процессах и использовании системы, это можно сделать воспользовавшись командой pip install psutil .

В результате выполнения данного кода я получил следующий результат(ваш может выглядеть иначе):

До вызова функции использовалось 13 MB памяти, а после создания списка из 1000000 элементов занимаемая память выросла до 196 MB. При этом время необходимое на выполнение операции составило 13,6 секунды.

Теперь давайте используем функцию-генератор. Разница в коде совсем небольшая, как вы можете заметить ниже:

А вот какие результаты я получил на своем компьютере при выполнении этого кода:

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

Надеюсь после прочтения статьи вы стали лучше понимать для чего используется ключевое слово yield в Python, а так же преимущества использования генераторов в вашей работе.

Что такое yield return?

Оператор yield return один из самых малоизвестных среди программистов C#. По крайней мере среди начинающих. И даже те, кто о нем кое-что знает, до конца не уверены, что правильно понимают принцип его работы. Этот досадный пробел обязательно нужно исправить. И, я надеюсь, эта статья вам поможет с этим.

Оператор yield return возвращает элемент коллекции в итераторе и перемещает текущую позицию на следующий элемент. Наличие оператора yield return превращает метод в итератор. Каждый раз, когда итератор встречает yield return он возвращает значение.

Этот оператор сигнализирует нам и компилятору, что данное выражение – итератор. Задача итератора перемещаться между элементами коллекции и возвращать значение текущего. Многие привыкли называть счетчик в цикле итератором, но это не так, ведь счетчик не возвращает значение.

Итератор преобразуется компилятором в «конечный автомат», который отслеживает текущую позицию и знает, как «переместиться» на следующую позицию. При этом значение элемента последовательности вычисляется в момент обращения к нему.

Вот простейший пример итератора:

Итераторы могут возвращать только тип IEnumerable<>.

Итераторы являются синтаксическими ярлыками для более сложного шаблона перечислителя. Когда компилятор C # встречает итератор, он расширяет его содержимое в CIL-код, который реализует шаблон перечислителя. Такая инкапсуляция существенно экономит время программиста.

Первый вопрос, который возникнет у неискушенного программиста: «Зачем мне использовать итератор? Я прекрасно могу выводить последовательность и без него».

Конечно можете. Различие в подходах. Итератор позволяет делать так называемое «ленивое вычисление». Это значит, что значение элемента вычисляется только когда он запрашивается.

Чтобы лучше понять, как работает yield return, мы сравним его с традиционными циклами. На примерах все станет понятно.

1) Обратите внимание, что с yield return нам не нужно создавать дополнительный список, чтобы заполнить его значениями. А значит мы получаем экономию памяти, ведь нам требуется лишь память для текущего элемента коллекции. При поэлементной обработке не выделяется память, достаточно кэша.

2) Возможность не вычислять результат для всего перечисления. Это главное преимущество. Вы помните, что yield return возвращает значение в момент его обработки? В этом примере мы бесконечно генерируем числа:


Вы уже видите разницу? Сейчас все поймете:

Мы используем LINQ оператор Take, чтобы ограничить количество выборки. В случае с yield return цикл остановится на пятом элементе.

А заполнение списка прервать нельзя. В результате получим ошибку Out of memory.

3) Возможность корректировать значения коллекции после выполнения итератора. Так как yield return возвращает элемент коллекции на момент реальной обработки (при отображении значения элемента в консоли, например), то мы можем изменять элементы коллекции даже после выполнения итератора. Ведь итератор на самом деле не возвращает реальные значения, когда вы его вызываете. Итератор знает где взять значения. И он их вернет только тогда, когда они реально потребуются. Это так называемая Lazy load.

А теперь вызовем эти методы:

После инициализации переменных MultipleYieldReturn и MultipleLoop добавим в коллекцию еще один элемент:

Посмотрите на результат теперь:

На момент вывода результатов в консоль коллекция содержала значение 4. Так как yield return выдает значения в момент их запроса, итератор обработал все актуальные значения. Традиционный цикл выполнился при инициализации переменной MultipleLoop, а на тот момент коллекция содержала всего 3 значения.

4) Обработка исключений с yield return имеет нюансы. Оператор yield return нельзя использовать в секции try-catch, только try-finally.

Например, как бы мы стали писать, не зная об ограничении:

В таком варианте блок catch никогда не отловит ошибку. Все дело в отложенном выполнении yield return. Об ошибке мы узнаем только в момент реальной работы с данными от итератора. Например, когда выводим данные из итератора на консоль.До тех пор итератор не работает с реальными данными.

Если вам все же нужно «отловить» ошибку в этом итераторе, то можно поступить так:

Говоря о yield return нельзя не упомянуть о втором операторе с yield. Это yield break. По своему назначению он аналогичен оператору break, просто применяется только в итераторах. Вот небольшой пример:

Из примера видно, что по достижении значения 5 итератор завершится, но до тех пор будет исправно выдавать значения.

Давайте подведем итоги. Когда же нужно использовать yield return?

  • При перечислении объектов. Итератор будет работать быстрее, чем возвращаемая коллекция. Да и накладные расходы памяти ниже;
  • В бесконечных циклах. Используя метод Take() вы всегда можете ограничить выборку.

yield

a good yield of wheat — хороший урожай пшеницы

to increase the yield of the soil — улучшать плодородие [увеличивать урожайность\] почвы

yield of bonds [on shares\] — доходность облигаций [акций\]

yield of capital investments — фондоотдача

to yield a good return — приносить хороший доход

This land yields well [poorly\]. — Эта земля приносит хороший [плохой\] урожай.

to yield due praise to smb. — воздавать должное [хвалу\] кому-л.

The disease yields to treatment. — Эта болезнь поддается лечению.

The ground yielded under his feet. — Почва оседала под его ногами.

Англо-русский экономический словарь .

Смотреть что такое «yield» в других словарях:

Yield — may mean:* Crop yield, a measure of the output per unit area of land under cultivation * Maximum sustainable yield, the largest long term fishery catch that can be safely taken * Rolled throughput yield, a statistical tool in Six Sigma * Yield… … Wikipedia

yield — 1 / yēld/ vt: to produce as return from an expenditure or investment: furnish as profit or interest an account that yield s 6 percent vi 1: to give place or precedence (as to one having a superior right or claim) 2: to relinquish the floor of a… … Law dictionary

Yield — Yield, v. t. [imp. & p. p. ; obs. p. p. ; p. pr. & vb. n. .] [OE. yelden, [yogh]elden, [yogh]ilden, AS. gieldan, gildan, to pay, give, restore, make an offering; akin to OFries. jelda, OS. geldan, D. gelden to cost, to be … The Collaborative International Dictionary of English

Yield — Álbum de Pearl Jam Publicación 3 de febrero de 1998 Grabación de Febrero a Septiembre de 1997 en los estudios Litho y estudios Bad Animals Género(s) Rock Alternativo, Grung … Wikipedia Español

yield´er — yield «yeeld», verb, noun. –v.t. 1. a) to produce; bear: »This land yields good crops. Mines yield ores. SYNONYM(S): furnish, supply. b) to give in return; bring in: »an investment which yielded a large profit. c) to fill a need; furnish; afford … Useful english dictionary

Yield — bezeichnet: Ausbeute (Halbleitertechnik) Yield, der englische Begriff für Rendite All Risk Yield (Nettoanfangsrendite bei Immobilieninvestitionen) Yield Spread Analyse, der englische Begriff für die Portfolioanalyse Yield Compression, auch… … Deutsch Wikipedia

yield — [yēld] vt. [ME yelden < OE gieldan, to pay, give, akin to Ger gelten, to be worth < IE base * ghel tō, (I) give, pay] 1. to produce; specif., a) to give or furnish as a natural process or as the result of cultivation [an orchard that… … English World dictionary

yield — vb 1 produce, turn out, *bear Analogous words: *generate, engender, breed, propagate: create, *invent: form, shape, *make, fabricate, fashion 2 *relinqui … New Dictionary of Synonyms

Yield — Yield, v. i. 1. To give up the contest; to submit; to surrender; to succumb. [1913 Webster] He saw the fainting Grecians yield. Dryden. [1913 Webster] 2. To comply with; to assent; as, I yielded to his request. [1913 Webster] 3. To give way; to… … The Collaborative International Dictionary of English

yield — [n] production of labor crop, earnings, harvest, income, output, outturn, produce, profit, return, revenue, takings, turnout; concept 260 yield [v1] produce accrue, admit, afford, allow, beam, bear, blossom, bring forth, bring in, discharge, earn … New thesaurus

Yield — Yield, n. Amount yielded; product; applied especially to products resulting from growth or cultivation. A goodly yield of fruit doth bring. Bacon. [1913 Webster] … The Collaborative International Dictionary of English

yield

На этой странице

Ключевое слово yield используется для остановки и возобновления функций-генераторов ( function* или legacy generator function).

Синтаксис

Описание

Ключевое слово yield вызывает остановку функции-генератора и возвращает текущее значение выражения, указанного после ключевого слова yield . Его можно рассматривать как аналог ключевого слова return в функции-генераторе.

На самом деле ключевое слово yield возвращает объект с двумя параметрами, value и done . При этом, value является результатом вычисления выражения после yield , а done указывает верно ли была выполнена функция-генератор.

Во время остановки на операторе yield , выполнение кода в генераторе не возобновится, пока не будет вызван метод next() функции-генератора. Это предоставляет непосредственный контроль за выполнением генератора и возвратом его значений.

Примеры

Следующий фрагмент кода содержит определение функции-генератора и вспомогательной функции:

После того как тело функции-генератора определено, оно может использоваться для получения итератора:

В чем польза yield?

Возвращаем коллекцию с помощью yield .

Пример 2. Возвращаем коллекцию с помощью обычного листа.

Результат равнозначен, вопрос — зачем тогда вообще нужен yield , если можно обойтись таким кодом? Или yield используется там, где код с new List() по каким-то причинам невозможен?

3 ответа 3

Ну, отличие на самом деле кардинальное.

Дело в том, что в первом случае у вас ленивое, а во втором — энергичное вычисление ответа. Это значит, что элементы выходной последовательности в энергичном случае вычисляются все и сразу, а в ленивом случае — только когда запрошены и только те, что запрошены.

Давайте посмотрим, где с практической стороны есть разница.

Для случая ленивого вычисления вся последовательность не присутствует полностью в памяти. Это значит, что при поэлементной обработке у нас не выделяется память, и сохраняется cache locality:

Вычисляем функцию на всей последовательности, сравниваем расход памяти:

Затем, у нас довольно большие различия в смысле операций. Энергичные вычисления производятся в момент вызова функции, в то время как ленивые вычисления происходят в момент, когда вы пользуетесь результатом. А значит, для реального вычисления ленивой последовательности состояние аргументов будет взято на момент перечисления. Вот пример:

Смотрим на отличия:

Поскольку ленивое вычисление происходит при перечислении, мы видим, что при изменении последовательности ленивая версия подхватывает изменения.

Другой пример. Посмотрим, что будет, если мы не вычисляем всю последовательность. Вычислим одну и ту же последовательность энергично и лениво:

Берём только 2 элемента из результата:


Получаем такой вывод на консоль:

Видите разницу? Ленивый вариант прогнал цикл всего два раза, и не вычислял «хвост» последовательности.

Ещё одна разница между случаями — когда сообщаются ошибки. В случае энергичного вычисления они сообщаются сразу. В случае ленивого — лишь при перечислении результата. Пример:

Для того, чтобы получить «лучшее из обоих миров», то есть, ленивое вычисление, но энергичную проверку аргументов, проще всего разделить функцию на две: энергичную проверку и ленивое вычисление без проверки. Для современных версий C# удобно использовать вложенные функции:

Ещё один случай различия — зависимость от внешних данных в процессе вычисления. Следующий код пытается влиять на вычисления, изменяя глобальное состояние. (Это не очень хороший код, не делайте так в реальных программах!)

Мы видим, что изменение глобальных данных даже после формальной отработки ленивой функции может влиять на вычисления.

(Это ещё один аргумент в пользу того, что функциональное программирование и мутабельное состояние плохо сочетаются.)

Что делает ключевое слово» yield»?

какая польза от yield ключевое слово в Python? Что он делает?

например, я пытаюсь понять этот код 1 :

что происходит, когда метод _get_child_candidates называется? Возвращается ли список? Один элемент? Это снова называется? Когда прекратятся последующие вызовы?

1. Код исходит от Йохена Шульца( jrschulz), который сделал большая библиотека Python для метрических пространств. Это ссылка на полный источник: модуль mspace.

30 ответов

чтобы понять, что yield нет, вы должны понять, что генераторы есть. И прежде чем Генераторы придут iterables.

Iterables

когда вы создаете список, вы можете прочитать его элементы один за другим. Чтение его элементов один за другим называется итерацией:

mylist это типа Iterable. Когда вы используете понимание списка, вы создаете список, и поэтому iterable:

все, что вы можете использовать » for. in. » на это итерируемый; lists , strings файлы.

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

генераторы

генераторы являются итераторами, своего рода iterable вы можете повторять только один раз. Генераторы не хранят все значения в памяти, они генерируют значения на лету:

это то же самое, за исключением того, что вы использовали () вместо [] . Но ты! .. —56не может выполнить for i in mygenerator во второй раз, так как генераторы могут использоваться только один раз: они вычисляют 0, затем забывают об этом и вычисляют 1 и заканчивают вычисление 4, Один за другим.

доходность

yield — это ключевое слово, которое используется как return , за исключением функции возвратит a генератор.

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

мастер yield , вы должны понимать, что при вызове функции код, написанный в теле функции, не запускается. функция возвращает только объект генератора, это немного сложно :-)

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

теперь самое трудное:

в первый раз for вызывает объект генератора, созданный из вашей функции, он будет запускать код в вашей функции с самого начала, пока он не попадет yield , затем он вернет первое значение цикла. Затем каждый другой вызов будет запускать цикл, который вы написали в функции еще раз, и возвращать следующее значение, пока не будет никакого значения для возврата.

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

ваш код объяснил

этот код содержит несколько умных части:

цикл повторяется в списке, но список расширяется, пока цикл повторяется : -) это краткий способ пройти все эти вложенные данные, даже если это немного опасно, так как вы можете получить бесконечный цикл. В этом случае candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) исчерпывает все значения генератора, но while продолжает создавать новые объекты генератора, которые будут производить разные значения от предыдущих, так как он не применяется на том же узле.

на extend() метод-это метод объекта списка, который ожидает итерацию и добавляет свои значения в список.

обычно передаем ему список:

но в вашем коде он получает генератор, что хорошо, потому что:

  1. вам не нужно читать два значения.
  2. у вас может быть много детей, и вы не хотите, чтобы все они хранились в памяти.

и это работает, потому что Python не заботится, является ли аргумент метода списком или нет. Python ожидает iterables, поэтому он будет работать со строками, списками, кортежами и генераторами! Это называется утка набрав и является одной из причин, почему Python так круто. Но это уже другая история, другой вопрос.

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

управление истощением генератора

Примечание: для Python 3, Используйте print(corner_street_atm.__next__()) или print(next(corner_street_atm))

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

Itertools, ваш лучший друг!—42

модуль itertools содержит специальные функции для управления iterables. Всегда хотите дублировать генератор? Цепочка из двух генераторов? Группировать значения во вложенном списке с одной строкой? Map / Zip без создания еще одного списка?

Илон Маск рекомендует:  Модуль boot asm

потом просто import itertools .

пример? Давайте посмотрим возможные порядки заезда на скачки на четырех лошадях:

понимание внутренних механизмов итерации

итерация — процесс, подразумевающий iterables (реализация __iter__() метод) и итераторы (реализующие __next__() метод). Iterables-это любые объекты, из которых вы можете получить итератор. Итераторы-это объекты, которые позволяют выполнять итерации на iterables.

больше об этом в этой статье про как for циклы работы.

ярлык для нашарили yield

когда вы видите функцию yield заявления, применить этот простой трюк, чтобы понять, что произойдет:

  1. вставить строку result = [] в начале функции.
  2. заменить yield expr С result.append(expr) .
  3. вставить строку return result в нижней части функция.
  4. Yay-no more yield заявления! Прочитайте и выясните код.
  5. сравнить функция к первоначальному определению.

этот трюк может дать вам представление о логике работы функции, но что на самом деле происходит с yield значительно отличается от того, что происходит в подходе на основе списка. Во многих случаях подход к доходности будет намного более эффективным и быстрым. В других случаях этот трюк заставит вас застрять в бесконечном цикле, даже если исходная функция работает нормально. Читайте дальше, чтобы узнать больше.

не путайте свои итераторы, итераторы и генераторы

сначала итератор протокол — когда вы пишите

Python выполняет следующие два шага:

получает итератор для mylist :

вызов iter(mylist) -> это возвращает объект с next() метод (или __next__() в Python 3).

[это шаг, который большинство людей забывают рассказать о]

использует итератор для перебора элементов:

сохранить вызов next() метод iterator, возвращенный из Шага 1. Возвращаемое значение next() назначена x и тело цикла выполняется. Если исключение StopIteration поднимается изнутри next() , это означает, что в итераторе больше нет значений и цикл выходит.

правда в том, что Python выполняет вышеуказанные два шага в любое время к цикл содержание объекта — это может быть цикл for, но это также может быть код типа otherlist.extend(mylist) (где otherlist — это список Python).

здесь mylist это типа Iterable потому что он реализует итератор протокол. В пользовательском классе можно реализовать __iter__() метод, чтобы сделать экземпляры вашего класса iterable. Этот метод должен возвращать итератор. Итератор-это объект, с next() метод. Это возможно реализовать оба __iter__() и next() на том же классе, и есть __iter__() возвращение self . Это будет работать для простых случаев, но не тогда, когда вы хотите, чтобы два итератора зацикливались на одном объекте одновременно.

Итак, это протокол итератора, многие объекты реализуют этот протокол:


  1. встроенные списки, словари, кортежи, множества, файлы.
  2. пользовательские классы, реализующие __iter__() .
  3. генераторы.

обратите внимание, что a for loop не знает, с каким объектом он имеет дело — он просто следует протоколу итератора и рад получить элемент за элементом, как он вызывает next() . Встроенные списки возвращают свои элементы один за другим, словари возвращают ключи один за другим файлы возвращают строки по одному и т. д. И генераторы возвращаются. Ну вот где yield приходит в:

вместо yield заявления, если у вас есть три return заявления в f123() только первый будет выполнен, и функция выхода. Но!—34 не является обычной функцией. Когда f123() — это не верните любое из значений в операторах yield! Он возвращает объект-генератор. Кроме того, функция на самом деле не выходит — она переходит в приостановленное состояние. Когда for loop пытается выполнить цикл над объектом генератора, функция возобновляется из приостановленного состояния в следующей строке после yield он ранее возвращен, выполняет следующую строку кода, в этом случае a yield оператор и возвращает это как следующий элемент. Это происходит до тех пор, пока функция не выйдет, и в этот момент генератор поднимает StopIteration , и цикл завершается.

таким образом, объект генератора похож на адаптер — на одном конце он показывает протокол итератора, подвергая __iter__() и next() методы для того чтобы держать for петли счастлив. Однако на другом конце он запускает функцию достаточно, чтобы получить из нее следующее значение, и возвращает ее в приостановленный режим.

Зачем Использовать Генераторы?

обычно вы можете написать код, который не использует генераторы, но реализует ту же логику. Один вариант должен использовать трюк временного списка я упомянул раньше. Это не будет работать во всех случаях, например, если у вас есть бесконечные циклы, или это может сделать неэффективное использование памяти, когда у вас есть длинный список. Другой подход заключается в реализации нового iterable класса SomethingIter который сохраняет состояние в членах экземпляра и выполняет следующий логический шаг в нем next() (или __next__() в Python 3) Метод. В зависимости от логики, код внутри next() метод может выглядеть очень сложным и быть склонным к ошибкам. Здесь генераторы обеспечивают чистое и легкое решение.

подумайте об этом так:

итератор — это просто причудливый звучащий термин для объекта, который имеет метод next (). Таким образом, функция yield-ed заканчивается примерно так:

это в основном то, что интерпретатор Python делает с выше код:

для более глубокого понимания того, что происходит за кулисами, for цикл можно переписать так:

это делает больше смысла или просто запутать вас больше? :)

Я должен отметить, что это и упрощение для наглядности. :)

на yield ключевое слово сводится к двум простым фактам:

  1. если компилятор обнаруживает yield ключевое слово в любом месте внутри функции эта функция больше не возвращается через return заявление. вместо, это тут возвращает ленивый объект «отложенный список» называется генератором
  2. генератор является итерационным. Что такое типа Iterable? Это что-то вроде list или set или range или dict-view, с встроенный протокол для посещения каждого элемента в определенном порядке.

в двух словах: генератор-это ленивый, постепенно ожидающий список и yield операторы позволяют использовать нотацию функций для программирования значений списка генератор должен постепенно выплюнуть.

пример

определим функцию makeRange это так же, как у Python range . Зову makeRange(n) ВОЗВРАЩАЕТ ГЕНЕРАТОР:

чтобы заставить генератор немедленно вернуть ожидающие значения, вы можете передать его в list() (так же, как вы могли бы любой iterable):

сравнение примера с «просто возвращая список»

приведенный выше пример можно рассматривать как простое создание списка, к которому вы добавляете и возвращаете:

есть один большой разница, однако; см. последний раздел.

как вы могли бы использовать генераторы

повторяемое является последней частью постижения список, и все генераторы повторяемое, поэтому они часто используются как так:

чтобы лучше почувствовать генераторы, вы можете поиграть с itertools модуль (обязательно используйте chain.from_iterable , а не chain при необходимости). Например, можно даже использовать генераторы для реализации бесконечно длинных ленивых списков как itertools.count() . Вы можете реализовать свой собственный def enumerate(iterable): zip(count(), iterable) , или же сделать это с помощью yield ключевое слово в цикле while.

Пожалуйста, обратите внимание: генераторы могут быть использованы для многих вещей, таких как реализация coroutines или недетерминированное программирование или другие элегантные вещи. Тем не менее, точка зрения» ленивых списков», которую я представляю здесь, является наиболее распространенным использованием, которое вы найдете.

за кулисами

вот как » Python протокол итерации» работает. То есть, что происходит, когда вы делаете list(makeRange(5)) . Это то, что я описал ранее как «ленивый, инкрементный список».

встроенная функция next() просто называет объекты .next() функция, которая является частью «протокола итерации» и находится на всех итераторах. Вы можете вручную использовать next() функция (и другие части протокола итерации) для реализации причудливых вещей, как правило, за счет читаемости, поэтому старайтесь избегать делать что.

мелочи

обычно большинство людей не заботятся о следующих различиях и, вероятно, хотят прекратить читать здесь.

на языке Python-speak, an типа Iterable — это любой объект, который «понимает концепцию for-loop», как список [1,2,3] и итератор является конкретным экземпляром запрошенного for-loop как [1,2,3].__iter__() . А генератор точно так же, как любой итератор, за исключением для того, как это было написано (с синтаксисом функции).

когда вы запрашиваете итератор из списка, он создает новый итератор. Однако, когда вы запрашиваете итератор у итератора (что вы редко делаете), он просто дает вам копию самого себя.

таким образом, в маловероятном случае, что вы не в состоянии сделать что-то подобное.

. тогда помните, что генератор-это итератор; то есть это одноразовое использование. Если хочешь . использовать его, вы должны позвонить myRange(. ) снова. Если вам нужно использовать результат дважды, преобразуйте результат в список и сохраните его в переменной x = list(myRange(5)) . Те, кому абсолютно необходимо клонировать генератор (например, кто делает ужасающе хакерское метапрограммирование), могут использовать itertools.tee если это абсолютно необходимо, так как копируемый итератор Python PEP предложение по стандартам было отложено.

что значит yield ключевое слово do в Python?

Ответ Контур / Резюме

  • функции yield, когда позвонил, возвращает генератор.
  • генераторы являются итераторами, потому что они реализуют итератор протокол, так что вы можете перебирать их.
  • генератор также может быть направлена информация, что делает его концептуально coroutine.
  • в Python 3, Вы можете делегат от одного генератора к другому в обоих направлениях с yield from .
  • (приложение критикует несколько ответов, включая верхний, и обсуждает использование return в генераторе.)

генераторы:

yield только законно внутри функции определение и включение yield в определении функции возвращает генератор.

идея генераторов исходит из других языков (см. сноску 1) с различными реализациями. В генераторах Python выполнение кода равно замороженные в точке выхода. Когда генератор вызывается (методы обсуждаются ниже), выполнение возобновляется, а затем зависает при следующем выходе.

yield обеспечивает простой способ реализация протокола итератора, определяется следующими двумя методами: __iter__ и next (Python 2) или __next__ (Python 3). Оба этих метода сделать объект iterator, который можно ввести проверку с Iterator Абстрактные Базы Класс .

тип генератора является подтипом итератора:

и при необходимости мы можем напечатать-проверить так:

функция Ан Iterator это когда-то исчерпано, вы не можете повторно использовать или сбросить его:

вам придется сделать еще один, если вы хотите снова использовать его функциональность (см. сноску 2):

можно получить данные программно, например:

приведенный выше простой генератор также эквивалентен приведенному ниже-начиная с Python 3.3 (и не доступен в Python 2), Вы можете использовать yield from :

, yield from также позволяет делегацию subgenerators, которые будут объяснены в следующем разделе О делегировании сотрудничества с Sub-сопрограммы.

Coroutines:

yield формирует выражение, которое позволяет отправлять данные в генератор (см. сноску 3)

вот пример, обратите внимание на received переменная, которая будет указывать на данные, отправленные в генератор:

во-первых, мы должны поставить генератор в очередь со встроенной функцией, next . Оно будет вызовите соответствующий next или __next__ метод, в зависимости от версии Python вы используете:

и теперь мы можем отправить данные в генератор. (отправка None is то же самое, что позвонить next .) :

совместное делегирование подпрограммы с yield from

Итак, напомним, что yield from доступно в Python 3. Это позволяет нам делегировать корутины к субкорутине:

и теперь мы можем делегировать функции в суб-генератора и его можно использовать генератором, как и выше:

вы можете узнать больше о точной семантике yield from на PEP 380.

другие методы: закрыть и бросить


на close метод поднимает GeneratorExit в точке функция казнь была заморожена. Это также будет называться __del__ так вы можно поместить любой код очистки, где вы обрабатываете GeneratorExit :

вы также можете создать исключение, которое может быть обработано в генераторе или передается обратно пользователю:

вывод

я считаю, что я охватил все аспекты следующего вопроса:

что значит yield ключевое слово do in В Python?

получается, что yield много. Я уверен, что мог бы добавить еще больше. подробные примеры к этому. Если вы хотите больше или имеете конструктивную критику, дайте мне знать, комментируя под.

приложение:

критика верхнего / принятого ответа**

  • он запутался в том, что делает типа Iterable, просто используя список в качестве примера. Смотрите мои ссылки выше, но в резюме: интерфейс Iterable имеет __iter__ способ возврата итератор. Ан итератор предоставляет .next (Python 2 или .__next__ (Python 3) Метод, который неявно вызывается for петли, пока он не поднимает StopIteration , и после этого будет продолжать это делать.
  • затем он использует выражение генератора для описания того, что такое генератор. Поскольку генератор-это просто удобный способ создать итератор, оно только запутывает дело, и мы еще не добрались до yield часть.
  • на управление истощением генератора он называет .next метод, когда вместо этого он должен использовать функцию встроенной, next . Будет соответствующий уровень абстрагирования, потому что его код не работает в Python 3.
  • модуле itertools? Это не имело отношения к тому, что yield не на всех.
  • нет обсуждения методов, которые yield обеспечивает вместе с новой функциональностью yield from в Python 3. верхний / принятый ответ — очень неполный ответ.

критика ответа, предложив yield в выражении генератора или понимании.

грамматика в настоящее время позволяет любое выражение в понимании списка.

так как yield является выражением, некоторые рекламировали его как интересное использование в постижениях или выражении генератора-несмотря на цитирование не особенно хорошего вариант использования.

разработчики CPython core являются обсуждаем умоляюще пособия. Вот сообщение из списка рассылки:

30 января 2020 года в 19:05 Бретт Кэннон написал:

on Sun, 29 янв 2020 в 16: 39 Крейг Родригес написал:

я в порядке с любым подходом. Оставляя вещи такими, какие они есть в Python 3 не годится, ИМХО.

мой голос-это SyntaxError, так как вы не получаете то, что ожидаете от синтаксис.

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

с точки зрения получения там, мы, вероятно, хотите:

  • SyntaxWarning или DeprecationWarning в 3.7
  • Py3k предупреждение в 2.7.x
  • SyntaxError в 3.8

Твое Здоровье, Ник.

— Nick Coghlan / ncoghlan at gmail.com / Брисбен, Австралия

далее, есть нерешенный вопрос (10544) который, кажется, указывает в направлении этого никогда хорошая идея (PyPy, реализация Python, написанная на Python, уже вызывает синтаксические предупреждения.)

итог, пока разработчики о CPython скажите нам иначе:не ставить yield в выражении генератора или понимании.

на return заявление в генераторе

в функции генератора, return оператор не может включать expression_list . В этом контексте голый return указывает, что генератор выполнен и вызовет StopIteration должен быть поднят.

An expression_list is в принципе любое количество выражений, разделенных запятыми, по сути, в Python 2, Вы можете остановить генератор с return , но вы не можете вернуть значение.

в функции генератора, return оператор указывает, что генератор выполнен и вызовет StopIteration должен быть поднят. Возвращаемое значение (если есть) используется в качестве аргумента для построения StopIteration становится StopIteration.value атрибут.

сноски

языки CLU, Sather и Icon были указаны в предложении чтобы представить концепцию генераторов Python. Общая идея что функция может поддерживать внутреннее состояние и давать промежуточное точки данных по требованию пользователя. Это обещало быть главный в представлении к другим подходам, включая Python threading, который даже не доступен на некоторых системный.

это означает, что, например, xrange объекты ( range в Python 3) не Iterator ы, хоть они и повторяемое, потому что они могут быть повторно использованы. Как списки, их __iter__ методы возвращают итератор объектов.

yield первоначально был представлен как утверждение, что означает, что он могла появиться только в начале строки в блоке кода. Теперь yield создает выход выражение. https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt Это изменение было предложил разрешить пользователю отправлять данные в генератор так же, как его можно получить. Чтобы отправить данные, нужно иметь возможность назначить их чему — то, и для этого заявление просто не сработает.

yield Как return — он возвращает все, что вы ему скажете (как генератор). Разница в том, что при следующем вызове генератора выполнение начинается с последнего вызова yield заявление. В отличие от возвращения,кадр стека не очищается, когда происходит выход, однако управление передается обратно вызывающему объекту, поэтому его состояние возобновится в следующий раз, когда функция.

в случае вашего кода, функция get_child_candidates действует как итератор, чтобы при расширении списка он добавлял по одному элементу в новый список.

list.extend вызывает итератор, пока он не исчерпан. В случае образца кода, который вы опубликовали, было бы намного яснее просто вернуть кортеж и добавить его в список.

есть одна дополнительная вещь, чтобы упомянуть: функция, которая дает на самом деле не должна завершаться. Я написал такой код:

тогда я могу использовать его в другом коде, как это:

это действительно помогает упростить некоторые проблемы и облегчает работу с некоторыми вещами.

для тех, кто предпочитает минимальный рабочий пример, медитируйте на этом интерактивном Python сеанс:

Yield дает Вам генератор.

Как вы можете видеть, в первом случае foo держит весь список в памяти сразу. Это не большое дело для списка с 5 элементами, но что, если вам нужен список из 5 миллионов? Мало того, что это огромный пожиратель памяти, он также стоит много времени, чтобы построить в то время, когда функция вызывается. Во втором случае, bar просто дает Вам генератор. Генератор является итерируемым-что означает, что вы можете использовать его в цикле for, и т. д., Но каждое значение может доступ только один раз. Все значения также не хранятся в памяти одновременно; объект генератора «запоминает», где он был в цикле в последний раз, когда вы его назвали-таким образом, если вы используете итерацию, чтобы (скажем) сосчитать до 50 миллиардов, вам не нужно считать до 50 миллиардов сразу и хранить 50 миллиардов чисел для подсчета. Опять же, это довольно надуманный пример, вы, вероятно, использовали бы itertools, если бы действительно хотели сосчитать до 50 миллиардов. :)

Это самый простой случай использования генераторов. Как вы сказали, его можно использовать для написания эффективных перестановок, используя yield для выталкивания вещей через стек вызовов вместо использования какой-то переменной стека. Генераторы можно также использовать для специализированного обхода дерева, и всего вида других вещей.

он возвращает генератор. Я не очень хорошо знаком с Python, но я считаю, что это то же самое, что и блоки итераторов C# Если вы знакомы с ними.

здесь статья IBM что объясняет его достаточно хорошо (для Python) насколько я могу видеть.

ключевая идея заключается в том, что компилятор / интерпретатор / все, что делает какой-то обман, чтобы, насколько это касается вызывающего абонента, они могли продолжать вызывать next (), и он будет продолжать возвращаемые значения —как будто метод генератора был приостановлен. Теперь, очевидно, вы не можете действительно «приостановить» метод, поэтому компилятор строит государственную машину, чтобы вы помнили, где вы сейчас находитесь и как выглядят локальные переменные и т. д. Это намного проще, чем написать итератор самостоятельно.

TL; DR

вместо этого:

этого:

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

это был мой первый» ага » момент с выходом.

yield — это сладкий способ сказать

доходность однопроходный: вы можете выполнить итерацию только один раз. Когда функция имеет выход, мы называем ее функции генератора. И итератор это то, что он возвращает. Это показательно. Мы теряем удобство контейнера, но получаем силу произвольно длинной серии.

доходность лень, это откладывает вычисления. Функция с выходом в ней фактически не выполняется на все!—47 когда вы называете его. Объекта итератора возвращает использует магия для поддержания внутреннего контекста функции. Каждый раз, когда вы звоните next() на итераторе (это происходит в цикле for) выполнение дюймов вперед к следующему выходу. ( return поднимает StopIteration и заканчивается серия.)

доходность универсальная. Он может делать бесконечные петли:

Если вам нужно несколько проходов и серия не слишком Лонг, просто позвони list() на:

блестящий выбор слова yield , потому что оба значения применение:

доходность — произведите или обеспечьте (как в земледелии)

. укажите следующие данные в серии.

доходность — уступить или отказаться (как в политической власти)

. отказаться от выполнения CPU до итератор продвигается.


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

на yield оператор в Python возвращает генератор. Генератор в Python-это функция, которая возвращает продолжения (и, в частности, тип корутины, но продолжения представляют собой более общий механизм для понимания того, что происходит на.)

продолжения в теории языков программирования являются гораздо более фундаментальным видом вычислений, но они не часто используются, потому что они чрезвычайно трудно рассуждать, а также очень трудно реализовать. Но идея о том, что такое продолжение, проста: это состояние вычисления, которое еще не завершено. В этом состоянии сохраняются текущие значения переменных, операции, которые еще предстоит выполнить, и так далее. Затем в какой-то момент позже программа продолжение может быть вызвана, так что переменные программы сбрасываются в это состояние и выполняются операции, которые были сохранены.

продолжения, в этой более общей форме, могут быть реализованы двумя способами. В call/cc путь, стек программы буквально сохраняется, а затем, когда вызывается продолжение, стек восстанавливается.

в continuation passing style (CPS) продолжения являются обычными функциями (только в языках, где функции первого класса), которые программист явно управляет и передает подпрограммам. В этом стиле состояние программы представлено закрытиями (и переменными, которые в них кодируются), а не переменными, которые находятся где-то в стеке. Функции, которые управляют потоком управления, принимают продолжение в качестве аргументов (в некоторых вариантах CPS функции могут принимать несколько продолжений) и манипулируют потоком управления, вызывая их, просто вызывая их и возвращая впоследствии. Очень простой пример стиля прохождения продолжения выглядит следующим образом:

Илон Маск рекомендует:  Заметки о разном

в этом (очень упрощенном) примере программист сохраняет операцию фактической записи файла в продолжение (что потенциально может быть очень сложной операцией со многими деталями для записи), а затем передает это продолжение (i.e, как первоклассное закрытие) другому оператору, который выполняет еще некоторую обработку, а затем вызывает его при необходимости. (Я использую этот шаблон много фактическое Программирование GUI, либо потому, что оно экономит строки кода, либо, что более важно, для управления потоком управления после запуска событий GUI.)

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

теперь давайте поговорим о генераторах в Python. Генераторы-это определенный подтип продолжения. Тогда как продолжения могут в генерал, чтобы сохранить состояние вычисление (т. е. стек вызовов программы), генераторы могут сохранять состояние итерации только через итератор. Хотя это определение несколько вводит в заблуждение для некоторых случаев использования генераторов. Например:

это явно разумная итерация, поведение которой хорошо определено-каждый раз, когда генератор повторяет ее, он возвращает 4 (и делает это навсегда). Но вероятно, это не прототипический тип iterable, который приходит на ум при мысли об итераторах (т. е. for x in collection: do_something(x) ). Этот пример иллюстрирует мощность генераторов: если что-то является итератором, генератор может сохранить состояние своей итерации.

чтобы повторить: продолжения могут сохранить состояние стека программы, а генераторы могут сохранить состояние итерации. Это означает, что продолжения намного мощнее генераторов, но также и то, что генераторы намного проще. Их легче реализовать дизайнеру языка, и они легче использовать программисту (если у вас есть время, чтобы записать, попробуйте прочитать и понять эта страница о продолжениях и вызове/cc).

но вы можете легко реализовать (и концептуализировать) генераторы как простой, конкретный случай продолжения стиля передачи:

, когда yield вызывается, он сообщает функции, чтобы вернуть продолжение. Когда функция вызывается снова, она начинается с того места, где остановились. Итак, в псевдо-псевдокоде (т. е. не псевдокоде, но не коде) генератор next метод в основном выглядит следующим образом:

здесь yield ключевое слово на самом деле синтаксический сахар для функции реального генератора, в основном что-то вроде:

помните, что это просто псевдокод, и фактическая реализация генераторов в Python более сложна. Но в качестве упражнения, чтобы понять, что происходит, попробуйте использовать продолжение прохождения стиля для реализации генераторных объектов без использования yield ключевое слово.

вот пример на простом языке. Я обеспечу соответствие между человеческими концепциями высокого уровня и концепциями Python низкого уровня.

  • я звоню вам и говорю, что мне нужна последовательность чисел, которая производится определенным образом, и я дам вам знать что такое алгоритм.
    этот шаг соответствует def ining функция генератора, т. е. функция, содержащая yield .
  • некоторое время спустя я говорю вам: «хорошо, приготовьтесь рассказать мне последовательность чисел».
    этот шаг соответствует вызову функции генератора, которая возвращает объект генератора. обратите внимание, что вы пока не говорите мне никаких чисел; вы просто хватаете бумагу и карандаш.
  • я спрашиваю вас: «скажите мне следующее номер», и вы говорите мне первый номер; после этого вы ждете, пока я спрошу у вас следующий номер. Это ваша работа-помнить, где вы были, какие числа вы уже сказали, и какой следующий номер. Меня не волнуют детали.
    этот шаг соответствует вызову .next() на объект генератора.
  • . повторите предыдущий шаг, пока.
  • в конце концов, вы можете прийти к концу. Ты не называешь мне номер, ты просто кричишь: «держись! лошади! Я молодец! Больше никаких цифр!»
    этот шаг соответствует объекту генератора, заканчивая свою работу, и воспитание StopIteration исключение функция генератора не должна вызывать исключение. Он возникает автоматически, когда функция заканчивается или выдает return .

это то, что делает генератор (функция, которая содержит yield ); он начинает выполнение, приостанавливается всякий раз, когда он делает yield , и когда попросил .next() значение продолжается от дело было в последнем. Он идеально подходит по дизайну с протоколом итератора Python, который описывает, как последовательно запрашивать значения.

самым известным пользователем протокола iterator является for команда в Python. Итак, всякий раз, когда вы делаете:

неважно sequence — это список, строка, словарь или генератор объект как описано выше; результат тот же: Вы читаете элементы последовательности по одному один.

отметим, что def ining функция, которая содержит yield ключевое слово-не единственный способ создать генератор, это просто самый простой способ создать.

для получения более точной информации прочитайте о типы iterator, the оператор yield и генераторы в документации Python.

в то время как многие ответы показывают, почему вы должны использовать yield чтобы создать генератор, есть больше применений для yield . Довольно легко сделать корутину, которая позволяет передавать информацию между двумя блоками кода. Я не буду повторять ни одного из прекрасных примеров, которые уже были даны об использовании yield для создания генератора.

, чтобы помочь понять, что такое yield в следующем коде, вы можете использовать ваш палец, чтобы проследить цикл через любой код, который имеет yield . Каждый раз, когда ваш палец попадает в yield , вы должны ждать next или send для ввода. Когда next вызывается, вы отслеживаете через код, пока не нажмете yield . код справа от yield оценивается и возвращается вызывающему объекту. затем вы ждете. Когда next вызывается снова, вы выполняете другой цикл через код. Однако, вы заметите, что в сопрограмма, yield можно также использовать с send . который отправит значение от вызывающего абонента на функция податливости. Если a send , то yield получает отправленное значение и выплевывает его с левой стороны. затем трассировка через код прогрессирует, пока вы не нажмете yield снова (возвращая значение в конце, как если бы next называлась).

есть еще yield и значение (начиная с версии Python 3.3):

для генератора предлагается синтаксис для делегирования части его операций другому генератору. Это позволяет учитывать раздел кода, содержащий «выход», и помещать его в другой генератор. Кроме того, генератору разрешено возвращать значение, и это значение доступно для делегирующего генератора.

новый синтаксис также открывает некоторые возможности для оптимизации, когда один генератор повторно выдает значения, полученные другим.

чтобы избежать сопрограммы путают с обычным генератором (сегодня yield используется в обоих).

я собирался опубликовать «прочитайте страницу 19» Python: Essential Reference «Бизли для быстрого описания генераторов», но многие другие уже опубликовали хорошие описания.

кроме того, обратите внимание, что yield можно использовать в сопрограммы как двойной их использования в функции генератора. Хотя это не то же самое использование, что и фрагмент кода, (yield) может использоваться как выражение в функции. Когда вызывающий объект отправляет значение в метод, используя send() метод, затем корутина выполню до следующего (yield) встречается инструкция.

генераторы и coroutines-отличный способ настроить приложения типа потока данных. Я подумал, что было бы полезно узнать о другом использовании yield заявление в функциях.

вот некоторые примеры Python о том, как на самом деле реализовать генераторы, как если бы Python не предоставлял синтаксический сахар для них:

как генератор Python:

использование лексических замыканий вместо генераторов

использование замыканий объектов вместо генераторов (поскольку ClosuresAndObjectsAreEquivalent)

С точки зрения программирования, итераторы реализуются как thunks.

для реализации итераторов, генераторов и пулов потоков для параллельного выполнения и т. д. в качестве thunks (также называемых анонимными функциями) используются сообщения, отправленные объекту закрытия, у которого есть диспетчер, и диспетчер отвечает на «сообщения».

«далее» сообщение отправлено закрытие, созданное «iter» позвонить.

существует множество способов реализации этого вычисления. Я использовал мутацию, но это легко сделать без мутации, вернув текущее значение и следующий урожай.

вот демонстрация, которая использует структуру R6RS, но семантика абсолютно идентична Python. Это та же самая модель вычисления, и для ее перезаписи требуется только изменение синтаксиса Питон.

все отличные ответы, однако немного сложно для новичков.

я предполагаю, что вы усвоили return заявление.

по аналогии, return и yield Близнецы. return означает «возврат и остановка», тогда как «выход» означает «возврат, но продолжить»

см., вы получаете только одно число, а не a их список. return никогда не позволяет вам преобладать счастливо, просто реализует один раз и выйти.

заменить return С yield :

теперь вы выигрываете, чтобы получить все номера.

по сравнению с return , который выполняется один раз и останавливается, yield работает раз вы планировали. Вы можете интерпретировать return as return one of them и yield as return all of them . Это называется iterable .

  1. еще один шаг, мы можем переписать yield заявление return

это ядро о yield .

разница между списком return выходы и объект yield вывод:

вы всегда получите [0, 1, 2] из объекта списка, но только могли бы получить их из » объекта yield выход один раз. Итак, у него новое имя generator объект, как показано в Out[11]: .

в заключение, как метафора к гроку:

  • return и yield Близнецы
  • list и generator Близнецы

вот простой пример:

Я не разработчик Python, но он выглядит для меня yield удерживает положение потока программы и следующий цикл начинается с позиции «выход». Кажется, что он ждет на этой позиции, а перед этим возвращает значение снаружи, и в следующий раз продолжает работать.

это, кажется, интересная и хорошая способность: D

вот мысленный образ чего yield делает.

мне нравится думать о потоке как о стеке (даже если он не реализован таким образом).

когда вызывается нормальная функция, она помещает свои локальные переменные в стек, выполняет некоторые вычисления, затем очищает стек и возвращает. Значения его локальных переменных больше никогда не будут видны.

С , когда код начинает работать (т. е. после вызова функции, возвращающей генератор объекта, чей next() затем вызывается метод), он аналогично помещает свои локальные переменные в стек и вычисляет некоторое время. Но затем, когда он попадает в yield оператор, прежде чем очистить свою часть стека и вернуться, он делает снимок своих локальных переменных и сохраняет их в объекте генератора. Он также записывает место, где он в настоящее время находится в своем коде (т. е. конкретный yield заявления).

так что это своего рода замороженная функция, что генератор держится.

, когда next() вызывается впоследствии, он извлекает вещи функции в стек и повторно анимирует его. Функция продолжает вычислять с того места, где она остановилась, не обращая внимания на тот факт, что она только что провела вечность в холодильнике.

сравните следующие примеры:

когда мы вызываем вторую функцию, она ведет себя совсем иначе, чем первая. The yield заявление может быть недоступен, но если она присутствует везде, она меняет природу того, с чем мы имеем дело.


вызов yielderFunction() не запускает свой код, но делает генератор из кода. (Может быть, это хорошая идея, чтобы назвать такие вещи с yielder префикс для удобства чтения.)

на gi_code и gi_frame поля, где хранится замороженное состояние. Исследуя их с dir(..) , мы можем подтвердить, что наша ментальная модель выше заслуживает доверия.

как подсказывает каждый ответ, yield используется для создания генератора последовательности. Он используется для генерации некоторой последовательности динамически. Например, при чтении файла строка за строкой в сети можно использовать

Yield является объектом

A return в функции будет возвращено одно значение.

если вы хотите функция для возврата огромного набора значений используйте yield .

что еще более важно, yield это барьер.

как барьер в языке CUDA, он не передаст управление до тех пор пока он не получит завершенный.

то есть он будет запускать код в вашей функции из начало, пока он не попадает yield . Затем он вернет первое значение цикла.

затем каждый другой вызов будет запускать цикл, который вы написали в функции еще раз, возвращая следующее значение, пока не будет никакого значения для возврата.

в целом, yield оператор преобразует вашу функцию в фабрику, которая производит специальный объект, называемый generator , который оборачивается вокруг тела вашей исходной функции. Когда generator повторяется, он выполняет свои функции, пока не достигнет следующего yield затем приостанавливает выполнение и оценивает значение, переданное yield . Он повторяет этот процесс на каждой итерации, пока путь выполнения не выйдет из функции. Например,

сила приходит от использования генератора с петлей которая высчитывает последовательность, генератор исполняет петлю останавливая каждый раз для того чтобы «дать» следующий результат вычисления, в этом путе она высчитывает список на лету, преимущество быть памятью сохраненной для особенно больших вычислений

скажем, вы хотели создать свой собственный range функция, которая производит итерационный диапазон чисел, вы можете сделать это так Итак,

и использовать его как это:

но это неэффективно, потому что

  • вы создаете массив, который вы используете только один раз (это тратит память)
  • этот код фактически повторяет этот массив дважды! :(

к счастью, Гвидо и его команда были достаточно щедры, чтобы разработать генераторы, так что мы могли просто сделать это;

теперь на каждой итерации функция на генераторе называется next() выполняет функцию до тех пор, пока она не достигнет оператора «yield», в котором она останавливается и «дает» значение или достигает конца функции. В этом случае по первому звонку, next() выполняет до оператора yield и yield ‘n’, при следующем вызове он выполнит оператор increment, вернется к «while», оценит его, и если true, он остановится и снова даст «n», он будет продолжать так до тех пор, пока условие while не вернет false и генератор не перейдет к концу функция.

yield — Это как возвращаемый элемент для функции. Разница в том, что yield элемент превращает функцию в генератор. Генератор ведет себя точно так же, как функция, пока что-то не «дано». Генератор останавливается до следующего вызова и продолжается с той же точки, с которой он начался. Вы можете получить последовательность всех «данных» значений в одном, вызвав list(generator()) .

(мой ответ ниже говорит только с точки зрения использования генератора Python, а не основная реализация механизма генератора, который включает в себя некоторые трюки манипуляции стеком и кучей.)

, когда yield вместо return в функции python эта функция превращается в нечто особенное, называемое generator function . Эта функция вернет объект generator тип. на yield ключевое слово-это флаг для уведомления компилятора python относиться к такой функции особенно. обычные функции завершатся, как только из него будет возвращено некоторое значение. Но с помощью компилятора, функция генератора можно подумать о как возобновляемые. То есть контекст выполнения будет восстановлен, и выполнение продолжится с последнего запуска. Пока вы явно не вызовете return, который вызовет StopIteration исключение (которое также является частью протокола итератора) или достигает конца функции. Я нашел много ссылок о generator но это один С functional programming perspective самый дигестэйбл.

(теперь я хочу поговорить о причинах generator и iterator на основе моего собственного понимания. Надеюсь, это поможет вам понять основные мотивации итераторов и генераторов. Такого понятия в других языках, таких как C#.)

как я понимаю, когда мы хотим обработать кучу данных, мы обычно сначала храним данные где-то, а затем обрабатывать их один за другим. Но это!—26интуитивное подход является проблематичным. Если объем данных огромен, то хранить их в целом заранее дорого. поэтому вместо хранения data непосредственно, почему бы не сохранить какой-то metadata косвенно, то есть the logic how the data is computed .

существует 2 подхода для обертывания таких метаданных.

  1. подход OO, мы обертываем метаданные as a class . Это так называемый iterator кто реализует протокол итератора (т. е. __next__() и __iter__() методов). Это также часто встречается итератор шаблон.
  2. функциональный подход, мы обертываем метаданные as a function . Это так называемые generator function . Но под капотом, вернувшийся generator object еще IS-A итератор, потому что он также реализует итератор протокол.

в любом случае, как iterator создается, т. е. некоторый объект, который может дать вам данные, которые вы хотите. Подход OO может быть немного сложным. В любом случае, какой из них использовать-решать вам.

многие люди используют return , а не yield , но в некоторых случаях yield может быть более эффективным и простым в работе.

вот пример yield определенно лучшая для:

возвращение (в функции)

доходность (в функции)

как функции делают то же самое, но yield использует три строки вместо пяти и имеет одну переменную меньше, чтобы беспокоиться.

как вы можете видеть обе функции делают то же самое. Единственная разница — return_dates() дает список и yield_dates() дает генератор.

примером реальной жизни было бы что-то вроде чтения файл построчно или если вы просто хотите сделать генератор.

на yield ключевое слово просто собирает возвращаемые результаты. Подумайте о yield Как return +=

вот простой yield основанный подход, чтобы вычислить ряд Фибоначчи, объяснил:

когда вы вводите это в свой REPL, а затем пытаетесь вызвать его, вы получите загадочный результат:

это потому, что присутствие yield сигнализировал Python, что вы хотите создать генератор, то есть, объект, который генерирует значения по требованию.

Итак, как вы генерируете эти значения? Это можно либо сделать непосредственно с помощью встроенной функции next , или косвенно путем подачи его в конструкцию, которая потребляет значения.

использование встроенного next() функция, вы непосредственно вызываете .next / __next__ , заставляя генератор вырабатывать значение:

косвенно, если вы предоставите fib до for петля, a list инициализатор, a tuple инициализатор или что-нибудь еще, что ожидает объект, который генерирует / производит значения, вы будете «потреблять» генератор до тех пор, пока он не сможет получить больше значений (и он вернется):

аналогично, с tuple инициализатор:

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

при первом вызове fib называя его:

Python компилирует функцию, встречает yield ключевое слово и просто возвращает объект-генератор на вас. Кажется, не очень полезно.

когда вы затем запрашиваете, он генерирует первое значение, прямо или косвенно, он выполняет все операторы, которые он находит, пока не встретит yield , затем он возвращает значение, которое вы предоставили yield и пауз. Для пример, который лучше демонстрирует это, давайте использовать print вызовов (заменить print «text» если на Python 2):

теперь введите REPL:

теперь у вас есть объект генератора, ожидающий команды для его создания значения. Использовать next и посмотреть, что печатается get:

результаты неупомянутой, что напечатано. Цитируемый результат-это то, что возвращается из yield . Звоните next теперь снова:

генератор помнит, что он был остановлен на yield value и выходе оттуда. Следующее сообщение печатается и поиск yield заявление, чтобы сделать паузу на нем выполняется снова (из-за while loop).

итератор на список: next() возвращает следующий элемент списка

итератор-генератор: next() вычислит следующий элемент на лету (выполнить код)

вы можете увидеть выход / генератор как способ вручную запустить поток управления извне (например, продолжить цикл один шаг), позвонив next , однако сложный поток.

Примечание: генератор не нормальная функция. Он запоминает предыдущее состояние, как локальные переменные (стека). Подробные объяснения см. в других ответах или статьях. Генератор может быть только повторяется на один раз. Вы могли бы обойтись без yield , но это было бы не так приятно, поэтому его можно считать «очень хорошим» языком сахара.

Зачем надо использовать ключевое слово yield

Объясните мне, зачем надо использовать ключевое слово yield? Уже мозг кипит. К примеру здесь:

В MSDN прочитал, как то совсем не внятно написано.

23.02.2012, 14:07

Зачем использовать ключевое слово static?
Здравствуйте. Зачем использовать ключевое слово static? И в каких ситуациях нужно использовать.

Ключевое слово yield
Есть такой код. Первые два Console.WriteLine() все понятно. а вот два последние вывода в консоль.

Ключевое слово yield в C#, есть ли его аналог в VB
Есть ли в Visual Basic аналог этого ключевого слова из C#? Тему перенес из Visual Basic.

Зачем и когда нужно писать ключевое слово static
Не как не могу понять зачем и когда нужно писать ключевое слово static. Может вы мне подскажите ?

В чём смысл событий? А именно, зачем нужно ключевое слово event
В чём прикол событий? А именно, зачем нужно ключевое слово event здесь, если все операции в примере.

23.02.2012, 14:30 2 23.02.2012, 14:54 [ТС] 3

Посмотрите что я написал в конце поста. мне ссылка на мсдн не надо, я это и сам видел и читал.

Добавлено через 16 минут

23.02.2012, 15:14 4

Решение

Добавлено через 37 секунд
Поставь точку останова во втором примере на строке 11 (на это листенги строка 11).

Добавлено через 1 минуту
В первом варианте ты сразу получишь всю последовательность, а во втором примере при каждом обращении к MoveNext() ты будешь получать следующий элемент последовательности возвращаясь в метод.

23.02.2012, 15:14
23.02.2012, 15:30 [ТС] 5
15.08.2014, 14:57 6
15.08.2014, 15:12 7

Решение

tranquil, dimiby, если у нас нет yield:
1) мы обрабатываем какой-то контейнер данных, его изменять нельзя(то ли еще понадобится то ли вовсе невозможно, напр. string, как массив символов)
2) для этого мы создаем контейнер и заполняем его полностью результатом обработки ВСЕГО входного контейнера данных.
Например, у нас есть контейнер int, нам надо получит квадрат каждого числа — в результате мы получим еще один массив в памяти как и исходный(по занимаемой памяти, а то и больше)
3) мы не можем работать с результатом пока не обработается ВЕСЬ входной контейнер

у нас есть yield:
1) мы возвращаем не контейнер результатов, а итератор на результаты
2) в памяти в один момент хранится результат обработки только одного элемента от полученного контейнера
3) мы не ждем пока обработается ВЕСЬ контейнер входящих данных.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL