Цикл повторяет один и тот же блок кода несколько раз. В Python их два, и выбор между ними механический. for перебирает готовую последовательность и делает ровно столько витков, сколько в ней элементов. while крутится, пока условие остаётся истинным, и сколько будет витков, заранее неизвестно. Весь код ниже выполнен на CPython 3.14.5, вывод приведён как есть.
На каждом обороте переменная получает следующий элемент
name получает один следующий элемент списка. Когда элементы заканчиваются, цикл завершается.Как написать цикл for
Заголовок for, имя переменной, ключевое слово in, источник, двоеточие. Тело сдвигается вправо на четыре пробела:
users = ["Аня", "Борис", "Вера"]
for name in users:
print(name)
Аня
Борис
Вера
На каждом витке name получает следующий элемент списка. Само имя произвольное, но осмысленное читается лучше: for name in users понятнее, чем for x in users.
Отступ здесь не оформление, а синтаксис. Он и решает, что входит в тело цикла, а что выполнится один раз после него:
for name in ["Аня", "Борис"]:
print("привет,", name)
print("всех поприветствовали")
привет, Аня
привет, Борис
всех поприветствовали
Убери отступ у первой строки, и Python откажется запускать файл:
IndentationError: expected an indented block after 'for' statement on line 2
Забудешь двоеточие в конце заголовка, получишь другое сообщение, тоже до запуска:
SyntaxError: expected ':'
Обе ошибки разобраны подробнее в статьях про ошибку отступа и SyntaxError.
Цикл по числам: range
range задаёт последовательность чисел, не создавая список. Одним аргументом он считает от нуля до указанного числа, не включая его:
for i in range(3):
print(i)
0
1
2
С двумя аргументами появляется начало, с тремя — шаг. Отрицательный шаг считает назад:
print(list(range(3)))
print(list(range(1, 4)))
print(list(range(0, 10, 2)))
print(list(range(3, 0, -1)))
[0, 1, 2]
[1, 2, 3]
[0, 2, 4, 6, 8]
[3, 2, 1]
Правая граница не входит в диапазон никогда. range(1, 4) даёт три числа, а не четыре, и это самый частый источник ошибки «на единицу» у новичков.
Если граница недостижима, цикл просто не выполнится ни разу, ошибки не будет:
print(list(range(0)))
print(list(range(5, 1)))
print(list(range(1, 5, -1)))
[]
[]
[]
Сумма чисел от 1 до 100 циклом и одной строкой:
total = 0
for n in range(1, 101):
total += n
print(total)
print(sum(range(1, 101)))
5050
5050
range хранит параметры диапазона, а не все его элементы. В этом замере сам объект range(1_000_000) занимает 48 байт, контейнер списка — около 8 МБ. getsizeof не включает память чисел, на которые ссылается список:
import sys
print(sys.getsizeof(range(1_000_000)), sys.getsizeof(list(range(1_000_000))))
48 8000056
Отсюда правило: писать for i in range(n) дёшево при любом n, а for i in list(range(n)) смысла не имеет.
Как перебрать строку, список и словарь
Циклом обходится любой объект, который умеет отдавать элементы по одному, включая открытый файл. Строка отдаёт символы:
for ch in "код":
print(ch)
к
о
д
Словарь по умолчанию отдаёт ключи. Чтобы получить пару, нужен метод .items(), и остальные его приёмы собраны в статье про словари:
prices = {"хлеб": 60, "молоко": 90}
for key in prices:
print(key)
for key, value in prices.items():
print(key, value)
хлеб
молоко
хлеб 60
молоко 90
Число перебрать нельзя, и Python скажет об этом прямо:
TypeError: 'int' object is not iterable
Чтобы повторить действие n раз, число оборачивают в range.
enumerate и zip: индекс и две последовательности
Когда рядом с элементом нужен его номер, берут enumerate. Второй аргумент задаёт, с чего начинать счёт:
users = ["Аня", "Борис", "Вера"]
for i, name in enumerate(users, start=1):
print(i, name)
1 Аня
2 Борис
3 Вера
Начинающие вместо этого пишут for i in range(len(users)) и достают элемент по индексу. Результат тот же, читается хуже, и на каждом обращении users[i] можно промахнуться границей. enumerate даёт сразу и номер, и элемент.
Две последовательности перебирают вместе через zip:
users = ["Аня", "Борис", "Вера"]
scores = [5, 4, 3]
for name, score in zip(users, scores):
print(name, score)
Аня 5
Борис 4
Вера 3
Тихая ловушка zip: если списки разной длины, он молча останавливается на коротком. Данные теряются без единого предупреждения. Начиная с Python 3.10 у него есть аргумент strict, который превращает несовпадение в ошибку:
print(list(zip(["Аня", "Борис", "Вера"], [5, 4])))
try:
list(zip(["Аня", "Борис", "Вера"], [5, 4], strict=True))
except ValueError as exc:
print(f"ValueError: {exc}")
[('Аня', 5), ('Борис', 4)]
ValueError: zip() argument 2 is shorter than argument 1
Когда длины обязаны совпадать, strict=True стоит ставить всегда: без него расхождение всплывёт кварталом позже как недостача в отчёте.
Цикл while: пока условие истинно
while проверяет условие перед каждым витком и выполняет тело, пока оно истинно. Сколько будет витков, определяют сами данные:
balance = 1000
days = 0
while balance > 0:
balance -= 300
days += 1
print(days, balance)
4 -200
Обрати внимание на ответ: денег хватило на 3 полных дня, а цикл сделал 4 витка и ушёл в минус. Условие проверяется до списания, а не после, поэтому на четвёртом витке остаток был ещё положительным, 100 рублей, и списание всё равно произошло. Если минус недопустим, условие пишут по будущему состоянию: while balance >= 300.
Условие может не выполниться ни разу, и тогда тело не отработает вовсе:
n = 10
while n < 5:
print("не выполнится ни разу")
print("цикл пропущен, n =", n)
цикл пропущен, n = 10
Главная опасность while в том, что переменную из условия меняешь ты сам. Забыл balance -= 300 внутри тела, и условие останется истинным навсегда: программа зависнет, ничего не напечатав. У for не нужно вручную обновлять счётчик, но конечность зависит от источника: range конечен, а генератор может выдавать значения бесконечно.
Бесконечный цикл и как из него выйти
Иногда бесконечный цикл пишут нарочно: while True с выходом по break. Так удобно, когда условий выхода несколько и все они внутри тела:
budget = 1000
bought = 0
items = [340, 220, 500, 120]
while True:
if not items:
break
price = items[0]
if budget - price < 0:
break
budget -= price
bought += 1
items.pop(0)
print(bought, budget, items)
2 440 [500, 120]
Куплено две позиции, осталось 440 рублей, и на третью за 500 денег уже не хватило. Заметь, что дешёвая позиция за 120 так и не куплена: цикл идёт по порядку и останавливается на первом же дорогом товаре. Это осознанный выбор алгоритма, а не ошибка, но именно такие развилки стоит проговаривать в условии задачи.
Правило безопасности простое. В теле while True должен быть хотя бы один break, до которого выполнение реально доходит, а если каждый выход спрятан под условием, которое никогда не станет истинным, цикл вечный и программа зависнет молча.
break и continue
break выходит из цикла целиком, continue пропускает остаток текущего витка и переходит к следующему.
prices = [120, 340, 90, 780, 200]
for price in prices:
if price > 500:
print("нашли дорогой:", price)
break
print("проверили:", price)
проверили: 120
проверили: 340
проверили: 90
нашли дорогой: 780
Цена 200 не проверялась: break оборвал цикл на четвёртом элементе. Так ищут первое подходящее значение, не тратя время на остальные.
prices = [120, 340, 90, 780, 200]
for price in prices:
if price < 200:
continue
print("подходит:", price)
подходит: 340
подходит: 780
подходит: 200
Здесь цикл прошёл все пять элементов, но для двух дешёвых сразу перескочил на следующий виток. continue — способ не заворачивать основную работу в глубокий if.
else у цикла: срабатывает, если break не случился
Конструкция редкая и на собеседованиях спрашивается часто. else после цикла выполняется тогда, когда цикл дошёл до конца сам, без break:
for price in [120, 340, 90, 780, 200]:
if price > 500:
print("нашли", price)
break
else:
print("дорогих нет")
нашли 780
for price in [120, 90, 200]:
if price > 500:
print("нашли", price)
break
else:
print("дорогих нет")
дорогих нет
Читать это надо как «если не нашли», а не как обычный else при if. Название неудачное, и путаница из-за него постоянная. Без else тот же поиск требует отдельного флага found = False, который выставляют перед break и проверяют после цикла.
Вложенные циклы
Цикл внутри цикла даёт все сочетания: внешний делает виток, внутренний прокручивается целиком.
for size in ["S", "M"]:
for color in ["красный", "синий"]:
print(size, color)
S красный
S синий
M красный
M синий
Число витков перемножается. Два списка по 1000 элементов дают миллион проходов, и такой цикл из безобидного превращается в главную причину тормозов.
Ключевая деталь: break рвёт только тот цикл, в котором стоит.
for size in ["S", "M"]:
for color in ["красный", "синий"]:
if color == "красный":
break
print(size, color)
print("внешний виток закончен:", size)
внешний виток закончен: S
внешний виток закончен: M
Ни одна пара не напечаталась, зато внешний цикл отработал оба витка полностью. Чтобы выйти сразу из двух, ставят флаг и проверяют его во внешнем цикле:
found = None
for size in ["S", "M"]:
for color in ["красный", "синий"]:
if (size, color) == ("M", "синий"):
found = (size, color)
break
if found:
break
print(found)
('M', 'синий')
Второй способ чище: вынести вложенные циклы в функцию и выйти из неё через return, который обрывает сразу всю вложенность, сколько бы уровней там ни было.
Ошибки, на которых спотыкаются
Изменение списка прямо во время обхода. Самая коварная, потому что программа не падает и выдаёт правдоподобный ответ:
nums = [1, 2, 2, 3, 4]
for n in nums:
if n == 2:
nums.remove(n)
print(nums)
[1, 2, 3, 4]
Одна двойка выжила. Цикл ходит по индексам, а remove сдвигает элементы влево, и после удаления первой двойки на её место встаёт вторая, которую счётчик уже проскочил. Остальные методы списка разобраны отдельно. Правильный путь — построить новый список, а не резать старый:
nums = [1, 2, 2, 3, 4]
nums = [n for n in nums if n != 2]
print(nums)
[1, 3, 4]
Со словарём то же самое кончается не тихой ошибкой, а падением:
RuntimeError: dictionary changed size during iteration
Здесь Python ловит нарушение сам, и это как раз удача: молчаливая порча данных обходится дороже.
Присваивание переменной цикла ничего не меняет. На каждом витке for заново кладёт в неё следующий элемент:
for i in range(3):
i = 10
print(i)
10
10
10
Витков всё равно три. Управлять количеством повторов через переменную цикла нельзя, для этого есть break.
Переменная остаётся жить после цикла. В отличие от списковых включений, обычный for не создаёт свою область видимости:
for i in range(3):
pass
print(i)
2
Значение последнего витка доступно снаружи. Если источник оказался пустым, переменная не создастся вовсе, и обращение к ней после цикла упадёт с NameError — о нём и о его близнеце UnboundLocalError есть отдельный разбор с правилом LEGB.
Когда цикл лучше не писать
Часть задач в Python решается без явного цикла, и это не про краткость, а про меньшее число мест для ошибки.
Сумма, минимум, максимум и счёт есть встроенными функциями: sum(nums), min(nums), max(nums), len(nums). Написанный руками цикл суммирования работает медленнее и даёт шанс ошибиться в начальном значении.
Построение нового списка из старого — работа для спискового включения: [x * 2 for x in nums] вместо трёх строк с append. Когда данных много и держать весь результат в памяти не нужно, тот же перебор делают генераторы.
Цикл остаётся там, где на каждом витке происходит что-то содержательное: запрос, запись в файл, накопление сложного состояния, ранний выход по условию.
Частые вопросы
Чем for отличается от while
for перебирает готовую последовательность и делает столько витков, сколько в ней элементов. while проверяет условие и крутится, пока оно истинно. Практическое правило такое. Знаешь заранее, по чему идёшь, бери for. Число витков зависит от вычислений внутри, бери while.
Как сделать цикл, который повторится n раз
for i in range(n). Если номер витка не нужен, имя переменной по традиции пишут подчёркиванием: for _ in range(n).
Как выйти из цикла досрочно
Оператором break. Он прерывает ближайший окружающий цикл; из вложенных выходят флагом или выносят цикл в функцию и делают return.
Как пройти список с конца
for x in reversed(nums) или срезом nums[::-1]. Разница в памяти: reversed идёт по исходному списку, срез создаёт его копию.
Что учить дальше
Если ещё не решил, ради чего всё это, посмотри обзор областей, где Python применяют на практике — от бэкенда и анализа данных до автоматизации.
Сразу после циклов идут списковые включения: та же работа короче, и половина учебных циклов схлопывается в одну строку. Дальше стоит разобраться, какие типы меняются на месте, а какие нет: именно оттуда растёт ошибка с изменением списка во время обхода.
В пути «Python с нуля» на Koddo под циклы отведён отдельный пак: «Счётчик активных пользователей» — простой for со счётчиком, «Закупка под лимит бюджета» — while с ранним выходом, «Самая длинная зелёная серия» — накопление состояния между витками. Проверить себя на живой задаче можно в дедупликации событий с сохранением порядка: там цикл должен помнить, что уже встречалось.