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

Итератор — более общая концепция. Это объект, у которого определены два метода: __next__ и __iter__.

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

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


generator = squares(a, b)

Либо с использованием так называемых generator expression — например, такого:

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

Если посмотреть dir(generator) и в первом, и во втором случае, то обнаружим, что оба варианта — итераторы (определены функции __next__ и __iter__).

По большому счёту, генераторы — это синтаксический сахар. И без них можно писать такие же эффективные программы, используя только итераторы. Вот только читаемость и простота кода сильно снизятся. Так что, если не нужно от итератора ничего сверх естественного, то стоит подумать над использованием генератора.

В примерах и объяснениях я использовал Python 3. Для второй версии языка есть незначительные отличия.