v1.0.0

Python с нуля для ЕГЭ · Итераторы и генераторы: значения по одному

Урок курсаPython 35 задачБесплатно

Итераторы и генераторы: значения по одному

Заглянем внутрь for, увидим исчерпание итератора и напишем генератор, который выдаёт значения по мере запроса.

Теория

for запрашивает значения по одному

Мы много раз перебирали строки, списки и range. Все они итерируемые: у них можно попросить итератор. Итератор помнит текущую позицию и отдаёт следующее значение по запросу.

words = ["первое", "второе"]iterator = iter(words)print(next(iterator))print(next(iterator))

Цикл for делает эти запросы сам и останавливается, когда значения закончились. Обычно ручные iter и next не нужны, но эта модель объясняет поведение генераторов и файлов.

Итератор хранит уже пройденный путь

Разберём на примере
Прочитаем один итератор дважды
Итератор создан для списка [10, 20].
  1. Первый next возвращает 10 и сдвигает позицию.
  2. Второй next возвращает 20.
  3. Третий запрос сообщает StopIteration: значений больше нет.
  4. Новый iter([10, 20]) создаст новый путь с начала.
Проверьте себя
Что помнит итератор после одного вызова next?
Он помнит, что первое значение уже выдано, и следующий запрос должен продолжить с новой позиции.

Генератор вычисляет значение только к запросу

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

def even_numbers(limit):    number = 0    while number <= limit:        yield number        number += 2 for number in even_numbers(6):    print(number)

Такой подход полезен, когда значений много или они появляются постепенно. Для небольшого готового набора обычный список часто проще.

Не каждая последовательность должна быть ленивой

Как действовать
Выбираем представление
  1. Нужен повторный проход? Готовая коллекция легко перебирается снова.
  2. Нужны все значения сразу? Список удобен для индексов, длины и сортировки.
  3. Значений много? Генератор может выдавать их постепенно.
  4. Назовите момент вычисления. yield откладывает работу до очередного запроса.
Проверьте себя
Чем yield отличается от return в этом уроке?
return завершает обычный вызов, а yield отдаёт одно значение и сохраняет состояние для продолжения.

Практика

Назовите итерируемый объект

Что в выражении for letter in "код" является итерируемым объектом?

Подсказка
Это источник, из которого цикл получает символы.
Решение

Строка «код» является итерируемым объектом; цикл получает из неё символы.

Получите следующее значение

iterator = iter([4, 7]). Что вернёт первый next(iterator)?

Подсказка
Новый итератор начинает с первого элемента.
Решение

Первый запрос возвращает первый элемент списка — 4.

Заметьте исчерпание

Какое исключение сообщает, что next запросил значение у закончившегося итератора?

Подсказка
Название показано в разборе двух значений.
Решение

Итератор сообщает об окончании через StopIteration; for обрабатывает его автоматически.

Сохраните место генератора

Какое ключевое слово отдаёт одно значение и сохраняет выполнение функции для продолжения?

Подсказка
Это не return.
Решение

yield выдаёт очередное значение и сохраняет состояние генератора.

Измените пример even_numbers так, чтобы он начал с 2, и вызовите even_numbers(7). Какое последнее число выведет цикл?

Подсказка
Генератор прибавляет 2 и не должен превысить limit.
Решение

Генератор выдаст 2, 4 и 6; следующее значение 8 превышает limit 7.

Итог

Теперь for не выглядит магией: он получает итератор и запрашивает значения, пока они не закончатся.

Генераторы остаются дополнительным инструментом. В следующих уроках важнее будет выбирать ясное решение, а не обязательно самое ленивое или короткое.

Прогресс

0 / 5
x

Вы ещё не решали задания