Итераторы и генераторы — основа эффективной обработки последовательностей в Python. Исторически Python стремился упростить работу с потоками данных и избежать избыточного хранения больших коллекций в памяти. Сначала появилась поддержка итераторов через протоколы __iter__ и __next__, а затем — генераторы, позволяющие создавать простейшие итераторы с помощью конструкций на базе yield.
Проблема: часто требуется обрабатывать большие объемы данных (например, стриминг с файла или базы данных), что невозможно сделать удобно и эффективно, если загружать всё в память сразу. Обычные функции возвращают все результаты сразу, а создание своих итераторов через классы часто чересчур громоздко для простых случаев.
Решение: механизм yield позволяет организовать "ленивую" генерацию данных. Генератор-функция не возвращает список или другую коллекцию, а возвращает объект генератора — итератор, который вычисляет значения по мере необходимости.
Пример кода:
# Простой генератор def countdown(n): while n > 0: yield n n -= 1 for i in countdown(3): print(i) # 3, 2, 1
Ключевые особенности:
Можно ли использовать return и yield в одной функции?
Да, но return в генераторе завершает итерацию (выбрасывает StopIteration), а yield может использоваться сколько угодно раз.
def example(): yield 1 return # StopIteration
Почему генератор нельзя "перезапустить" после завершения?
Генератор после окончания итераций (StopIteration) не может быть перезапущен, его нужно создать заново.
gen = countdown(2) list(gen) # [2, 1] list(gen) # [] (генератор уже исчерпан)
В чем разница между генератором и итератором?
Генератор — частный случай итератора; любой объект с методами iter и next — итератор, но генератор создается через функцию с yield.
Разработчик написал функцию, загружающую миллион строк файла в память через list(fd) для анализа. Это привело к переполнению памяти на сервере.
Плюсы:
Минусы:
Использование генератора для строчного чтения файла (по строке за раз) и анализа данных на лету с помощью yield.
Плюсы:
Минусы: