Difference between Python's Generators and Iterators
Разница между генераторами и итераторами Python
В чем разница между итераторами и генераторами? Было бы полезно привести несколько примеров того, когда вы будете использовать каждый случай.
Переведено автоматически
Ответ 1
iterator это более общая концепция: любой объект, класс которого имеет __next__ метод (next в Python 2) и __iter__ метод, который это делает return self.
Каждый генератор является итератором, но не наоборот. Генератор создается путем вызова функции, которая имеет одно или несколько yield выражений (yield операторов в Python 2.5 и более ранних версиях) и является объектом, который соответствует определению an в предыдущем параграфе iterator.
Возможно, вы захотите использовать пользовательский итератор, а не генератор, когда вам нужен класс с несколько сложным поведением для поддержания состояния или вы хотите предоставить другие методы помимо __next__ (и __iter__ и __init__). Чаще всего достаточно генератора (иногда, для достаточно простых нужд, выражения генератора), и его проще кодировать, потому что поддержание состояния (в разумных пределах) в основном "делается за вас" путем приостановки и возобновления фрейма.
Например, такой генератор, как:
defsquares(start, stop): for i inrange(start, stop): yield i * i
generator = squares(a, b)
или эквивалентное выражение генератора (genexp)
generator = (i*i for i inrange(a, b))
потребуется больше кода для создания пользовательского итератора:
def__next__(self): # next in Python 2 if self.start >= self.stop: raise StopIteration current = self.start * self.start self.start += 1 return current
iterator = Squares(a, b)
Но, конечно, с помощью class Squares вы могли бы легко предложить дополнительные методы, т.е.
defcurrent(self): return self.start
если у вас есть какая-либо реальная потребность в такой дополнительной функциональности в вашем приложении.
Ответ 2
В чем разница между итераторами и генераторами? Было бы полезно привести несколько примеров того, когда вы будете использовать каждый случай.
Вкратце: Итераторы - это объекты, у которых есть метод __iter__ и a __next__ (next в Python 2). Генераторы предоставляют простой встроенный способ создания экземпляров итераторов.
Функция с yield в ней по-прежнему является функцией, которая при вызове возвращает экземпляр объекта-генератора:
Мы можем создать генератор несколькими способами. Очень распространенный и простой способ сделать это - с помощью функции.
В частности, функция с yield в ней - это функция, которая при вызове возвращает генератор:
>>> defa_function(): "just a function definition with yield in it" yield >>> type(a_function) <class'function'> >>> a_generator = a_function() # when called >>> type(a_generator) # returns a generator <class'generator'>
для которой требуется __iter__ метод, возвращающий итератор:
>>> collections.Iterable() Traceback (most recent call last): File "<pyshell#79>", line 1, in <module> collections.Iterable() TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__
Некоторыми примерами итераций являются встроенные кортежи, списки, словари, наборы, замороженные наборы, строки, байтовые строки, байтовые массивы, диапазоны и представления памяти:
>>> all(isinstance(element, collections.Iterable) for element in ( (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b''))) True
Для итераторов требуется метод next or __next__
В Python 2:
>>> collections.Iterator() Traceback (most recent call last): File "<pyshell#80>", line 1, in <module> collections.Iterator() TypeError: Can't instantiate abstract class Iterator with abstract methods next
И в Python 3:
>>> collections.Iterator() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Iterator with abstract methods __next__
Мы можем получить итераторы из встроенных объектов (или пользовательских объектов) с помощью iter функции:
>>> all(isinstance(iter(element), collections.Iterator) for element in ( (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b''))) True
Метод __iter__ вызывается, когда вы пытаетесь использовать объект с циклом for. Затем метод __next__ вызывается для объекта-итератора, чтобы получить каждый элемент для цикла. Итератор запускаетсяStopIteration, когда вы исчерпали его, и в этот момент его нельзя использовать повторно.
Из документации
Из раздела " Типы генераторов" раздела " Типы итераторов" встроенной документации по типам:
Генераторы Python предоставляют удобный способ реализации протокола итератора. Если __iter__() метод объекта-контейнера реализован как генератор, он автоматически вернет объект-итератор (технически, объект-генератор), предоставляющий методы __iter__() и next() [__next__() в Python 3]. Более подробную информацию о генераторах можно найти в документации к выражению yield.
(Выделено мной.)
Итак, из этого мы узнаем, что генераторы являются (удобным) типом итератора.
Примеры объектов итератора
Вы можете создать объект, реализующий протокол Iterator, создав или расширив свой собственный объект.
Вы можете использовать протокол Iterator напрямую, когда вам нужно расширить объект Python как объект, который можно повторять.
Однако в подавляющем большинстве случаев вам лучше всего подходит использование yield для определения функции, которая возвращает итератор генератора, или для рассмотрения выражений генератора.
Итераторы - это объекты, которые используют next() метод для получения следующих значений последовательности.
Генераторы - это функции, которые генерируют последовательность значений с использованием yield ключевого слова.
Каждый next() вызов метода для объекта-генератора (например: f ниже), возвращаемый функцией-генератором (например: foo() ниже), генерирует следующее значение в последовательности.
Когда вызывается функция-генератор, она возвращает объект-генератор, даже не начиная выполнение функции. Когда next() метод вызывается в первый раз, функция начинает выполняться до тех пор, пока не достигнет yield оператора, который возвращает заданное значение. yield Отслеживает, что произошло, т. Е. Запоминает последнее выполнение. И, во-вторых, next() вызов продолжается с предыдущего значения.
Следующий пример демонстрирует взаимодействие между yield и вызовом next метода в объекте-генераторе.
>>> deffoo(): ... print("begin") ... for i inrange(3): ... print("before yield", i) ... yield i ... print("after yield", i) ... print("end") ... >>> f = foo() >>> next(f) begin before yield0# Control is in for loop 0 >>> next(f) after yield0 before yield1# Continue for loop 1 >>> next(f) after yield1 before yield2 2 >>> next(f) after yield2 end Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Ответ 4
Добавляю ответ, потому что ни один из существующих ответов конкретно не устраняет путаницу в официальной литературе.
Функции генератора - это обычные функции, определенные с помощью yield вместо return. При вызове функция-генератор возвращает объект-генератор, который является разновидностью итератора - у него есть next() метод. При вызове next() возвращается следующее значение, выдаваемое функцией-генератором.
Либо функция, либо объект могут называться "генератором" в зависимости от того, какой исходный документ Python вы читаете. В глоссарии Python говорится о функциях генератора, в то время как в Python wiki подразумеваются объекты генератора. В руководстве по Python удивительно удается отразить оба варианта использования в трех предложениях:
Генераторы - это простой и мощный инструмент для создания итераторов. Они написаны как обычные функции, но используют оператор yield всякий раз, когда хотят вернуть данные. Каждый раз, когда в нем вызывается функция next(), генератор возобновляет работу с того места, на котором он остановился (он запоминает все значения данных и какой оператор выполнялся последним).
Первые два предложения идентифицируют генераторы с функциями генератора, в то время как третье предложение идентифицирует их с объектами генератора.
Несмотря на всю эту путаницу, можно обратиться к справочнику по языку Python, чтобы получить четкое и окончательное объяснение:
Выражение yield используется только при определении функции-генератора и может использоваться только в теле определения функции. Использования выражения yield в определении функции достаточно, чтобы это определение создало функцию-генератор вместо обычной функции.
Когда вызывается функция-генератор, она возвращает итератор, известный как генератор. Затем этот генератор управляет выполнением функции-генератора.
Итак, в формальном и точном использовании, "генератор" безоговорочно означает объект генератора, а не функцию генератора.
генератор ... Обычно относится к функции генератора, но в некоторых контекстах может относиться к итератору генератора. В случаях, когда предполагаемое значение неясно, использование полных терминов позволяет избежать двусмысленности.