Меню

Python отличие генератора от итератора



Итерируемый объект, итератор и генератор в Python

В Python итерируемый объект (iterable или iterable object), итератор (iterator или iterator object) и генератор (generator или generator object) — разные понятия, а не синонимы одного и того же. От итерируемого объекта можно получить его «копию»-итератор; генератор является разновидностью итератора.

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

У итерируемого объекта, то есть объекта, который можно «превратить» в итератор, должен быть метод __iter__(), который возвращает соответствующий объект-итератор.

У итерируемого объекта нет метода __next__(), который используется при итерации:

У итератора есть метод __next__(), который извлекает из итератора очередной элемент. При этом этот элемент уже не содержится в итераторе. Таким образом, итератор в конечном итоге опустошается:

Метод __next__() исчерпанного итератора возбуждает исключение StopIteration.

У итераторов, также как у итерируемых объектов, есть метод __iter__(). Однако в данном случае он возвращает сам объект-итератор:

Здесь переменные b и c указывают на один и тот же объект.

Примеры итерируемых объектов в Python — список, словарь, строка и другие контейнерные типы (они же коллекции), тип, возвращаемый функцией range().

Примеры итераторов — файловые объекты, генераторы, итераторы созданные на основе списка, строки, объекта типа range и т. д.

В Python есть встроенные функции iter() и next(), которые соответственно вызывают методы __iter__() и __next__() объектов, переданных в качестве аргумента.

Внутренний механизм цикла for сначала вызывает метод __iter__() объекта. Так что, если передан итерируемый объект, создается итератор. После этого применяется метод __next__() до тех пор, пока не будет возбуждено исключение StopIteration.

Поскольку метод __iter__() итератора возвращает сам итератор, то после перебора циклом for объект исчерпывается. То есть получить данные из итератора можно только один раз. В случае с коллекциями это не так. Здесь создается другой объект — итератор. Он, а не итерируемый объект, отдается на обработку циклу for.

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

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

Рассмотрим пример. Определим сначала собственный класс-итератор:

Пример выполнения кода:

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

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

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

Нам незачем самим определять методы __iter__() и __next__(), так как они неявно присутствуют у генератора.

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

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

Генераторное выражение и функция-генератор возвращают объект одного и того же типа — generator.

Источник

Разница между генераторами и итераторами Python

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

iterator это более общая концепция: любой объект, класс которого имеет next метод ( __next__ в Python 3) и __iter__ метод, который имеет return self .

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

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

Читайте также:  Генератор трехфазного тока от 220в

Например, такой генератор, как:

или эквивалентный генератор выражения (genexp)

потребовалось бы больше кода для сборки в качестве пользовательского итератора:

Но, конечно, с классом Squares вы можете легко предложить дополнительные методы, т.е.

если у вас есть какая-либо реальная потребность в такой дополнительной функциональности в вашем приложении.

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

Итак, итераторы — это объекты, у которых есть метод __iter__ и __next__ ( next в Python 2). Генераторы предоставляют простой встроенный способ создания экземпляров итераторов.

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

Выражение генератора также возвращает генератор:

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

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

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

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

В частности, функция с yield в ней является функцией, которая при вызове возвращает генератор:

И генератор, опять же, является Итератором:

Итератор это итеративный

для чего требуется __iter__ метод, который возвращает Iterator:

Некоторыми примерами итераций являются встроенные кортежи, списки, словари, наборы, замороженные наборы, строки, байтовые строки, байтовые массивы, диапазоны и представления памяти:

Итераторы требуют next или __next__ метода

Мы можем получить итераторы из встроенных объектов (или пользовательских объектов) с помощью iter функции:

__iter__ Метод вызывается при попытке использовать объект с для петли. Затем __next__ вызывается метод объекта итератора, чтобы вывести каждый элемент в цикл. Итератор повышается, StopIteration когда вы исчерпали его, и он не может быть повторно использован в этой точке.

Из документации

Из разделе Типы генератора в разделе Типы Итератор Встроенные типы документации :

Генераторы Python предоставляют удобный способ реализации протокола итератора. Если __iter__() метод контейнерного объекта реализован как генератор, он автоматически вернет объект итератора (технически объект генератора), предоставляющий методы __iter__() и next() [ __next__() в Python 3]. Более подробную информацию о генераторах можно найти в документации по выражению yield.

Из этого мы узнаем, что Генераторы — это (удобный) тип Итератора.

Примеры объектов-итераторов

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

Но для этого проще просто использовать генератор:

Или, может быть, проще, выражение генератора (работает аналогично списку пониманий):

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

Вывод

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

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

Наконец, обратите внимание, что генераторы обеспечивают еще больше функциональности в качестве сопрограмм. Я объясню Генераторам вместе с yield утверждением подробно мой ответ на вопрос «Что делает ключевое слово yield»?

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

Генератор — это функция, которая создает или возвращает последовательность значений с использованием yield метода.

Каждый next() вызов метода для объекта генератора (например, f как в примере ниже), возвращаемый функцией генератора (для foo() функции ex в примере ниже), генерирует следующее значение в последовательности.

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

В следующем примере демонстрируется взаимодействие между yield и вызовом метода next для объекта генератора.

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

Функции генератора — это обычные функции, определенные yield вместо return . При вызове функция генератора возвращает объект генератора , который является своего рода итератором — у него есть next() метод. При вызове next() возвращается следующее значение, полученное функцией генератора.

Либо функцию, либо объект можно назвать «генератором», в зависимости от того, какой исходный документ Python вы прочитали. Словарь Python говорит функцию генератора, в то время как Python вики означает объекты генератора. Учебник Python замечательно удается подразумевать как использования в пространстве трех предложений:

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

Первые два предложения идентифицируют генераторы с функциями генератора, а третье предложение идентифицирует их с объектами генератора.

Читайте также:  Ветряные генераторы в сша

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

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

Когда вызывается функция генератора, она возвращает итератор, известный как генератор. Затем этот генератор контролирует выполнение функции генератора.

Таким образом, в формальном и точном использовании, термин «генератор» означает объект генератора, а не функцию генератора.

Приведенные выше ссылки относятся к Python 2, но ссылка на язык Python 3 говорит о том же. Тем не менее, глоссарий Python 3 утверждает, что

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

Источник

Итераторы и генераторы

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

Итератор – более общая концепция, чем генератор.

Итератор – это интерфейс доступа к элементам коллекций и потоков данных. Он требует реализации единственного метода – «дай мне следующий элемент». Если вы пишите свой итератор на Python 3 вам нужно реализовать в классе метод __next__. Если элементы исчерпаны итератор возбудит исключение StopIteration.

📎 Пример. Итератор счетчик – выдает числа от low до high:

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

Генератор – это итератор, но не наоборот. Не любой итератор является генератором.

Есть два способа получить генератор:

📎 1. Генераторное выражение (что-то типа list comprehension, но возвращает генератор, а не список). Используются круглые скобки:

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

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

Пример. Генератор чисел Фибоначчи (бесконечный):

Вызвав генераторную функцию fib() мы получили генератор. Затем мы итерируем этот генератор функцией next().

Остановка генератора

Если генератор «закончился» (т.е. просто вышли из функции генератора в конце его кода или по return), то автоматически возбуждается исключение StopIteration. Это не ошибка, это нормально, просто принятый способ обработки конца итератора.

for in сам ловит исключение StopIteration и просто завершает итерировать этот генератор.

Передача данных в генератор

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

send() – отправить данные в генератор. Переданное значение вернется из той конструкции yield, на которой возникла последняя пауза генератора. При этом генератор будет прокручен на один шаг, как если бы мы вызвали next:

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

Обратите внимание, что первый раз нельзя посылать в генератор данные, пока мы не прокрутили его до первого yield. Нужно либо взывать next(g) или g.send(None) – это одно и тоже.

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

throw() – бросить исключение внутри генератора. Исключение будет возбуждено из того выражение yield, где генератор последний раз остановился.

close() – закрыть генератор. Бросает внутри генератора особое исключение GeneratorExit. Это исключение, даже если оно не обработано, не распространится в код, вызвавший close(). Но, если мы поймали это исключение внутри генератора, то после закрытия генератора нельзя уже делать yield, рискуя получить RuntimeError. Остальные виды исключений будут распространяться из генератора в код, его вызывающий. Попытка итерировать закрытый итератор приведет к исключению StopIteration (закрытый генератор – пустой итератор).

Читайте также:  Дизельные генераторы в гомеле

Бонус

Как взять из итератора (в том числе из генератора) N первых значений?

Можно, конечно, написать свою функцию. Но зачем, если она уже есть в стандартном модуле itertools. Этот модуль содержит множество вспомогательных функций для работы с итераторами. Нам понадобится itertools.islice. Первый аргумент – итератор (ну или генератор), остальные три – как в range.

В первом примере мы передаем в функцию itertools.islice наш генератор чисел Фибоначчи и число чисел, которые надо вычислить (в нашем случае – 10).

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

Во втором примеры аргументов 4 штуки. В этом случае второй аргумент – начальный номер = 10, третий – конечный номер = 20 – (не включительно), и четвертый – шаг = 2. (Очень похоже на range, не так ли?)

Источник

Python. Урок 15. Итераторы и генераторы

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

Итераторы в языке Python

Во многих современных языках программирования используют такие сущности как итераторы. Основное их назначение – это упрощение навигации по элементам объекта, который, как правило, представляет собой некоторую коллекцию (список, словарь и т.п.). Язык Python, в этом случае, не исключение и в нем тоже есть поддержка итераторов. Итератор представляет собой объект перечислитель, который для данного объекта выдает следующий элемент, либо бросает исключение, если элементов больше нет.

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

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

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

Как видно из приведенного выше примера вызов функции next(itr) каждый раз возвращает следующий элемент из списка, а когда эти элементы заканчиваются, генерируется исключение StopIteration.

Создание собственных итераторов

Если нужно обойти элементы внутри объекта вашего собственного класса, необходимо построить свой итератор. Создадим класс, объект которого будет итератором, выдающим определенное количество единиц, которое пользователь задает при создании объекта. Такой класс будет содержать конструктор, принимающий на вход количество единиц и метод __next__(), без него экземпляры данного класса не будут итераторами.

В нашем примере при четвертом вызове функции next() будет выброшено исключение StopIteration. Если мы хотим, чтобы с данным объектом можно было работать в цикле for, то в класс SimpleIterator нужно добавить метод __iter__(), который возвращает итератор, в данном случае этот метод должен возвращать self.

Генераторы

Генераторы позволяют значительно упростить работу по конструированию итераторов. В предыдущих примерах, для построения итератора и работы с ним, мы создавали отдельный класс. Генератор – это функция, которая будучи вызванной в функции next() возвращает следующий объект согласно алгоритму ее работы. Вместо ключевого слова return в генераторе используется yield. Проще всего работу генератор посмотреть на примере. Напишем функцию, которая генерирует необходимое нам количество единиц.

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

Ключевым моментом для понимания работы генераторов является то, при вызове yield функция не прекращает свою работу, а “замораживается” до очередной итерации, запускаемой функцией next(). Если вы в своем генераторе, где-то используете ключевое слово return, то дойдя до этого места будет выброшено исключение StopIteration, а если после ключевого слова return поместить какую-либо информацию, то она будет добавлена к описанию StopIteration.

P.S.

Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.

Источник