8

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

32

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

5 ответ(ов)

8

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

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

Вам может понадобиться использовать пользовательский итератор, а не генератор, когда вам требуется класс с довольно сложным поведением, поддерживающим состояние, или если вы хотите предоставить другие методы помимо __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 в Python 2
        if self.start >= self.stop:
            raise StopIteration
        current = self.start * self.start
        self.start += 1
        return current

iterator = Squares(a, b)

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

def current(self):
    return self.start

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

0

Итераторы — это объекты, которые используют метод 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            # Управление находится в цикле for
0
>>> next(f)
after yield 0             
before yield 1            # Продолжаем выполнение цикла for
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
0

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

Если вы создаете свой собственный итератор, это немного более сложно — вам нужно создать класс и реализовать хотя бы методы __iter__ и __next__. Но что, если вы не хотите заниматься этой морокой и хотите быстро создать итератор? К счастью, Python предоставляет упрощенный способ определения итератора. Все, что вам нужно сделать, — это определить функцию с хотя бы одним вызовом yield, и теперь, когда вы вызываете эту функцию, она возвращает "что-то", что будет вести себя как итератор (вы сможете вызывать метод next и использовать его в цикле for). Это что-то в Python называется Генератором.

Надеюсь, это немного прояснит ситуацию.

0

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

Скажем так, мой личный ответ на первый вопрос был бы таким: у итерируемых объектов есть только метод __iter__, у обычных итераторов есть только метод __next__, а у генераторов — и __iter__, и __next__, а также дополнительный метод close.

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

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

0

Вот краткое руководство по генераторам в Python в формате ответов на StackOverflow:

  1. Генераторная функция — это функция, содержащая оператор yield.

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

  3. Генераторный объект (часто называемый просто "генератор") возвращается как из генераторной функции, так и из генераторного выражения.

  4. Генератор также является подтипом итератора.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь