Коротко: for в Python не считает числа, а перебирает готовую последовательность — список, строку, диапазон. Поэтому range(5) даёт 0, 1, 2, 3, 4, а не числа до пяти включительно. Ноль на старте и отсутствующее последнее значение — две вещи, на которых спотыкаются почти все.

Цикл — первая конструкция, после которой ребёнок пишет программу, а не последовательность команд. До него код читается сверху вниз, по строке за раз. После появляется другая мысль: пусть компьютер сделает это сто раз сам.

Что именно перебирает for

В записи for i in range(5) главное слово — in. Python берёт то, внутри чего есть элементы, и достаёт их по очереди. Список, строка, диапазон, строки файла, ключи словаря — всё это последовательности, и ведут себя они одинаково.

Поэтому for symbol in "кот" выдаст три буквы, а for number in [10, 20, 30] — три числа. Счётчика Python при этом не ведёт: переменная просто получает очередной элемент. Это совсем другая модель, чем в языках, где цикл состоит из начала, условия и шага, и дети, видевшие такую запись у старших, сначала ищут её глазами.

Имя переменной после for — произвольное. Традиционное i пришло из математики и годится для чисел, но в коде, который будут читать люди, лучше писать по сути: for uchenik in spisok_klassa понятнее, чем for i in spisok_klassa. На занятиях мы просим называть переменную так, чтобы строка читалась вслух как предложение.

range: четыре формы и одна ловушка

Формы записи range()
ЗаписьЧто даётКогда нужен
range(5)0, 1, 2, 3, 4повторить действие пять раз
range(1, 6)1, 2, 3, 4, 5когда счёт идёт с единицы
range(0, 10, 2)0, 2, 4, 6, 8через шаг: чётные, каждый третий
range(5, 0, -1)5, 4, 3, 2, 1обратный отсчёт

Правое число не входит в результат никогда. Это не прихоть языка: благодаря ей range(len(spisok)) даёт ровно столько шагов, сколько элементов в списке, и последняя позиция не вылетает за границы. Стоит показать это на примере со списком из трёх имён — тогда правило перестаёт быть произвольным.

Отдельно об отрицательном шаге. Запись range(10, 0, -1) даёт числа от десяти до единицы, и нуля в ней нет по той же причине: правое число не входит. Чтобы отсчёт закончился нулём, нужно range(10, -1, -1), и именно эта конструкция каждый раз вызывает удивление.

Типичная сцена на занятии: ребёнок пишет обратный отсчёт ракеты и получает «1, 0, Пуск» вместо «3, 2, 1, Пуск». Правка занимает секунду. Понимание, почему граница работает именно так, — минут пять. Эти пять минут потом экономят часы на задачах со срезами списков.

Когда нужен не элемент, а его номер

Школьники часто пишут for i in range(len(spisok)), чтобы добраться и до номера, и до значения. Работает, но читается хуже enumerate: запись for nomer, znachenie in enumerate(spisok) отдаёт оба сразу и не даёт ошибиться с индексом.

Если нумерация должна начинаться с единицы — например, для списка учеников на экране, — enumerate принимает второй аргумент: enumerate(spisok, 1). Мелочь, но она убирает из кода вечное nomer + 1, в котором легко ошибиться.

Когда надо пройти по двум спискам одновременно, пригодится zip: for imya, ball in zip(imena, bally). Он останавливается на более коротком из списков — это стоит проверить сразу, иначе часть данных тихо потеряется.

Словари перебираются иначе

Простой for key in slovar даёт только ключи. Чтобы сразу получить пару, нужен for key, value in slovar.items(). Дети, пропустившие эту деталь, обычно пишут slovar[key] внутри цикла — работает, но длиннее и запутаннее.

Вложенные циклы

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

Полезно сразу проговорить цену. Два вложенных цикла по сто шагов — это десять тысяч действий, три вложенных по сто — миллион. На маленьких примерах разница незаметна, но привычка считать шаги пригодится, когда программа начнёт тормозить.

break, continue и странное for-else

Два слова управляют ходом цикла. break выходит из него полностью, continue пропускает только текущий шаг и идёт к следующему. Путаница между ними даёт самую тихую категорию ошибок: программа работает, просто результат не тот.

Есть ещё конструкция, которой нет в большинстве языков: else после for. Блок выполняется, если цикл дошёл до конца без единого break. Чаще всего он нужен в поиске: перебрали весь список, ничего не нашли — сообщили об этом. Встретив такое в чужом коде, ребёнок обычно решает, что это ошибка.

Четыре ошибки, которые повторяются

  • Изменение списка прямо во время перебора. Элементы сдвигаются, часть пропускается молча. Безопаснее собирать новый список, а не править тот, по которому идёшь.
  • Отступ. Тело цикла в Python задаёт именно отступ, и строка, сдвинутая назад, выполнится один раз после цикла, а не внутри. Ошибка IndentationError — самая частая из первых недель.
  • Забытый input внутри цикла. Если запрос вынесен наружу, программа спрашивает один раз, а дальше крутит тот же ответ.
  • Бесконечный цикл через while там, где хватило бы for. Когда количество шагов известно заранее, for надёжнее: он не умеет зацикливаться.

Как читать ошибку, когда цикл не работает

Python почти всегда называет строку, на которой споткнулся, и тип проблемы. В циклах встречаются три сообщения. IndentationError означает, что отступы не совпадают — обычно смешались табуляция и пробелы, и на вид строка стоит правильно. TypeError: 'int' object is not iterable появляется, когда после in поставили число вместо range от него. IndexError: list index out of range — выход за границы списка, почти всегда из-за лишней единицы в range.

Приучать читать последнюю строку сообщения стоит с первого же занятия. Ребёнок, который этого не делает, зовёт преподавателя на каждое красное пятно; ребёнок, который читает, через месяц начинает исправлять половину ошибок сам. Разница в скорости обучения после этого становится заметна невооружённым глазом.

Когда цикл появляется на курсе

На курсе Python для 10–14 лет цикл идёт на втором-третьем занятии — ровно тогда, когда первые программы становятся длиннее экрана и переписывать одинаковые строки надоедает самому ребёнку. Момент удачный: мотивация приходит из задачи, а не из объяснения.

В старшей группе 14–18 лет тот же материал проходят быстрее и сразу переходят к генераторам списков — короткой записи, которая делает то же самое в одну строку. Но порядок сохраняется: сначала обычный цикл, потом сокращение. Обратный путь, когда подросток выучил сокращение и не понимает, что под ним, заканчивается списыванием без понимания.

О том, в каком возрасте всё это имеет смысл начинать, мы писали отдельно — в материале с какого возраста учить Python ребёнка.