Вопрос-Ответ

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__). Чаще всего достаточно генератора (иногда, для достаточно простых нужд, выражения генератора), и его проще кодировать, потому что поддержание состояния (в разумных пределах) в основном "делается за вас" путем приостановки и возобновления фрейма.

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

def squares(start, stop):
for i in range(start, stop):
yield i * i

generator = squares(a, b)

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

generator = (i*i for i in range(a, b))

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

class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop

def __iter__(self):
return self

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 вы могли бы легко предложить дополнительные методы, т.е.

def current(self):
return self.start

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

Ответ 2

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


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

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

def a_function():
"when called, returns generator object"
yield

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

a_generator = (i for i in range(0))

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

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

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

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

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

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

>>> def a_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'>

А генератор, опять же, это итератор:

>>> isinstance(a_generator, collections.Iterator)
True

Итератор - это итерируемый

Итератор - это итерируемый,

>>> issubclass(collections.Iterator, collections.Iterable)
True

для которой требуется __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, создав или расширив свой собственный объект.

class Yes(collections.Iterator):

def __init__(self, stop):
self.x = 0
self.stop = stop

def __iter__(self):
return self

def next(self):
if self.x < self.stop:
self.x += 1
return 'yes'
else:
# Iterators must raise when done, else considered broken
raise StopIteration

__next__ = next # Python 3 compatibility

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

def yes(stop):
for _ in range(stop):
yield 'yes'

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

yes_expr = ('yes' for _ in range(stop))

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

>>> stop = 4             
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop),
('yes' for _ in range(stop))):
... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes

Заключение

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

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

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

Ответ 3

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

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

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

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

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

>>> def foo():
... print("begin")
... for i in range(3):
... print("before yield", i)
... yield i
... print("after yield", i)
... print("end")
...
>>> f = foo()
>>> next(f)
begin
before yield 0 # Control is in for loop
0
>>> next(f)
after yield 0
before yield 1 # Continue for loop
1
>>> next(f)
after yield 1
before yield 2
2
>>> next(f)
after yield 2
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 в определении функции достаточно, чтобы это определение создало функцию-генератор вместо обычной функции.


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


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

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


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


python