В чем разница между итератором и генератором
Итератор — это объект, который поддерживает протокол итерации и позволяет последовательно перебирать элементы коллекции, а генератор — это функция с оператором yield, которая при вызове возвращает итератор. Другими словами, генератор — это удобный способ создания итератора, но не каждый итератор является генератором.
Как это работает
Итератор в Python — это объект, который реализует методы __iter__() и __next__(). Метод __iter__() возвращает сам итератор, а __next__() возвращает следующий элемент последовательности или выбрасывает исключение StopIteration, когда элементы закончились. Любая коллекция, например список или кортеж, поддерживает итерацию, но сама по себе не является итератором — она возвращает итератор при вызове iter().
Генератор — это функция, содержащая ключевое слово yield. При вызове такая функция не выполняет свой код сразу, а возвращает объект-генератор, который является итератором. Каждый вызов next() выполняет код функции до ближайшего yield, возвращает значение и приостанавливает выполнение. При следующем вызове выполнение продолжается с того места, где остановилось.
Пример
# Обычный итератор на основе класса
class Counter:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
raise StopIteration
# Генератор — функция с yield
def counter_gen(limit):
current = 0
while current < limit:
current += 1
yield current
# Использование
for num in Counter(3):
print(num) # 1, 2, 3
for num in counter_gen(3):
print(num) # 1, 2, 3Оба подхода дают одинаковый результат, но генератор требует меньше кода и проще для понимания.
Подводные камни
- Генератор — это одноразовый итератор: после полного перебора он исчерпывается, и повторный
forпо нему не даст результатов. Обычный итератор тоже может быть одноразовым, но его можно реализовать с возможностью сброса. - Генераторы вычисляют значения лениво, то есть по одному за раз. Это экономит память, но если нужно получить все значения сразу, придётся преобразовать генератор в список через
list(gen). - В генераторе нельзя использовать
returnс значением — это вызовет исключениеStopIterationс этим значением, но обычноreturnбез значения просто завершает генератор. - Генераторы не поддерживают индексацию и срезы, так как не хранят все элементы в памяти.
Когда использовать
Генераторы стоит применять, когда нужно обработать большой поток данных, который не помещается в память целиком, или когда вычисление каждого элемента занимает время и не хочется делать это заранее. Например, чтение файла построчно, бесконечные последовательности или конвейеры обработки данных. Обычные итераторы полезны, когда нужен более сложный контроль над процессом итерации или когда класс уже реализует другие методы, и добавление итерации — лишь часть его поведения.
Коротко
- Итератор — объект с методами
__iter__и__next__, генератор — функция сyield, возвращающая итератор. - Генератор — частный случай итератора, но не наоборот.
- Генераторы ленивы и экономят память, но одноразовы.
- Для простых случаев предпочитают генераторы, для сложной логики — явные итераторы.
