Циклы в Python: for, while и как сделать do while

Циклы в Python: for, while и как сделать do while Полезное

Цикл в Python — конструкция, которая повторяет блок кода: for перебирает элементы коллекции, while повторяет тело, пока условие истинно. Отдельного цикла do while (с проверкой условия после тела) в Python нет. Его записывают как while True: с проверкой условия в конце тела и выходом через break — так тело гарантированно выполнится хотя бы один раз.

Все примеры ниже проверены на Python 3.14.7, вывод взят из реального запуска.

Короткий ответ: do while в Python

В C, Java или JavaScript цикл do { ... } while (условие); сначала выполняет тело, потом проверяет условие. В Python то же поведение дает такой шаблон:

attempts = iter(["abc", "-5", "42"])   # имитация ввода пользователя

while True:
    raw = next(attempts)          # тело: выполняется минимум один раз
    print("ввод:", raw)
    if raw.isdecimal() and int(raw) > 0:
        break                     # условие выхода проверяется ПОСЛЕ тела

print("принято:", int(raw))

Вывод:

ввод: abc
ввод: -5
ввод: 42
принято: 42

Типичный сценарий — повторять запрос ввода, пока пользователь не введет корректное значение. В реальной программе вместо next(attempts) будет input("Число: ").strip(). Обратите внимание на инверсию: в do while пишут условие продолжения, а здесь в if стоит условие выхода.

Проверка isdecimal() отсекает минус, пробелы и символы вроде ² (у "²".isdigit() результат True, но int("²") падает с ValueError). Но и после isdecimal() int() может упасть: строка из 5000 цифр проходит проверку, а int отклоняет ее с ValueError (лимит 4300 цифр при переводе строки в число, с Python 3.11). Поэтому для реального ввода, в том числе с отрицательными числами, надежнее try: int(raw) с except ValueError плюс проверка диапазона.

Мини-словарь

  • Итерация — один проход тела цикла.
  • Итерируемый объект — то, что можно перебрать в for: список, строка, словарь, range, файл.
  • Предусловие — условие проверяется до тела (while): тело может не выполниться ни разу.
  • Постусловие — условие проверяется после тела (do while): тело выполнится минимум один раз.

Цикл for: перебор элементов

for в Python — это цикл «для каждого элемента», а не счетчик, как for (i = 0; i < n; i++) в C. Он берет элементы итерируемого объекта по очереди, пока они не кончатся.

langs = ["Python", "Go", "Rust"]
for lang in langs:
    print(lang, len(lang))

for i in range(1, 10, 3):
    print(i, end=" ")
print()

for number, lang in enumerate(langs, start=1):
    print(number, lang)

prices = {"кофе": 180, "чай": 120}
for name, price in prices.items():
    print(f"{name}: {price} руб.")

Вывод:

Python 6
Go 2
Rust 4
1 4 7 
1 Python
2 Go
3 Rust
кофе: 180 руб.
чай: 120 руб.

Разбор по частям:

  • range(start, stop, step) выдает целые числа от start до stop не включительно: range(1, 10, 3) дает 1, 4, 7. Один аргумент range(5) — это 0..4.
  • enumerate() выдает пары «номер, элемент» — вместо ручного счетчика и range(len(langs)).
  • dict.items() перебирает пары «ключ, значение». Порядок — порядок вставки (гарантирован для словарей с Python 3.7).

Когда брать for: известна коллекция или число повторений.

Цикл while: повтор по условию

while проверяет условие перед каждой итерацией. Подходит, когда число повторов заранее неизвестно: например, сколько месяцев нужно, чтобы вклад 1000 при росте 10% в месяц превысил 1500.

balance = 1000
month = 0
while balance < 1500:
    balance = balance * 1.1
    month += 1
print(month, round(balance, 2))

Вывод: 5 1610.51. Условие проверено шесть раз, тело выполнено пять.

Внутри тела обязательно должно меняться что-то, от чего зависит условие. Иначе цикл станет бесконечным. Остановить зависшую программу в терминале можно сочетанием Ctrl+C — Python прервет ее с исключением KeyboardInterrupt.

Сравнение: for, while и эмуляция do while

Конструкция Когда проверяется условие Минимум выполнений тела Типичная задача
for x in коллекция перед каждым элементом (есть ли следующий) 0 (пустая коллекция) обработать все элементы, повторить N раз
while условие: до тела 0 повторять, пока верно условие
while True: + if ...: break в конце после тела 1 запрос ввода, повтор операции до успеха
while (x := f()): до тела, по результату f() 0 читать данные, пока они есть

Последняя строка — оператор присваивания := (с Python 3.8). Это не do while: условие проверяется до тела, но функция вызывается в заголовке, и код не дублируется.

chunks = iter([b"abc", b"de", b""])   # имитация чтения файла кусками
while (chunk := next(chunks)):
    print(len(chunk), chunk)
print("конец данных")

Вывод:

3 b'abc'
2 b'de'
конец данных

Иногда do while эмулируют флагом (done = False, while not done:) или дублированием тела перед while. Оба варианта рабочие, но дублирование опасно: исправив одну копию, легко забыть вторую. Шаблон while True + break короче и держит тело в одном месте.

break, continue и else у цикла

  • break немедленно завершает ближайший цикл.
  • continue пропускает остаток тела и переходит к следующей итерации.
  • else после цикла выполняется, если цикл закончился сам, без break.
n = 0
while True:
    n += 1
    if n % 2 == 0:
        continue          # четные пропускаем, print ниже не выполнится
    if n > 7:
        break             # выход из цикла
    print(n, end=" ")
print("| вышли при n =", n)

Вывод: 1 3 5 7 | вышли при n = 9. При n = 8 сработал continue, до break дело дошло на n = 9.

Блок else удобен в поиске: он отвечает на вопрос «перебрали все и не нашли».

import math

def find_divisor(n):
    for d in range(2, math.isqrt(n) + 1):
        if n % d == 0:
            print(n, "делится на", d)
            break
    else:
        print(n, "- простое число")

find_divisor(91)
find_divisor(97)

Вывод:

91 делится на 7
97 - простое число

Граница: else выполнится и тогда, когда цикл не сделал ни одной итерации (пустой список, ложное с самого начала условие while). Функция выше рассчитана на целые n >= 2: для 0 и 1 цикл пустой, и else ошибочно назовет их простыми. Для них нужна отдельная проверка до цикла. Корень считаем через math.isqrt (целочисленный, с Python 3.8), а не int(n ** 0.5): для очень больших n дробный корень теряет точность, а при n больше примерно 10^308 падает с OverflowError: int too large to convert to float.

Вложенные циклы и выход сразу из двух

break прерывает только внутренний цикл. Чтобы выйти из обоих, вынесите их в функцию и используйте return:

grid = [
    [1, 3, 5],
    [7, 8, 9],
    [10, 12, 13],
]
odd_only = [
    [1, 3],
    [5, 7],
]

def first_even(matrix):
    for i, row in enumerate(matrix):
        for j, value in enumerate(row):
            if value % 2 == 0:
                return i, j, value    # return выходит сразу из обоих циклов
    return None

print(first_even(grid))
print(first_even(odd_only))

Вывод: (1, 1, 8) и None. Если вложенность нужна для перебора всех комбинаций, ее можно заменить одним циклом по itertools.product(["S", "M"], ["red", "blue"]) — он выдаст пары S red, S blue, M red, M blue.

Частые ошибки: неверно -> результат -> исправление

1. Удаление элементов списка во время перебора.

nums = [1, 2, 2, 3, 4]
for x in nums:
    if x % 2 == 0:
        nums.remove(x)
print(nums)

Результат: [1, 2, 3] — одна двойка осталась. После удаления элементы сдвигаются, и цикл перескакивает через следующий. Исправление — собрать новый список:

nums = [1, 2, 2, 3, 4]
nums = [x for x in nums if x % 2 != 0]
print(nums)

Результат: [1, 3].

2. Условие while, которое никогда не станет ложным. Цикл while x != 0: x -= 3 при x = 10 не остановится: значения 10, 7, 4, 1, -2 проскакивают ноль. Со страховкой на 100 шагов прогон печатает стоп: x = -293. Исправление — условие-неравенство while x > 0:, после цикла x равен -2.

3. Дробный шаг в range.

for x in range(0, 1, 0.25):
    print(x)

Результат:

TypeError: 'float' object cannot be interpreted as an integer

range работает только с целыми. Исправление — перебирать целый счетчик и делить:

for i in range(4):
    x = i / 4
    print(x, end=" ")
print()

Результат: 0.0 0.25 0.5 0.75.

Выводы

  • for перебирает элементы итерируемого объекта, while повторяет тело, пока условие истинно.
  • Цикла do while в Python нет; его заменяет while True: с if условие_выхода: break в конце тела.
  • break выходит из ближайшего цикла, continue пропускает итерацию, else у цикла срабатывает, если не было break (в том числе при нуле итераций).
  • Из вложенных циклов удобнее всего выходить через return из функции.
  • Не меняйте список, который перебираете, и следите, чтобы условие while могло стать ложным.

Где применяется / связь с практикой

Освойте тему на практике

Циклы стоят за любой обработкой данных: разбор файлов и логов, повтор сетевого запроса до успеха, обход строк таблицы, валидация пользовательского ввода. На курсе «Python-разработчик. Базовый уровень» циклы, функции и коллекции отрабатываются на практических задачах с ревью кода. Разобраться с темой в живом формате можно на открытых уроках Otus.

FAQ

Почему в Python нет do while?
Предложение расширить while блоком do:, который выполняется до проверки условия (PEP 315), было отклонено: по итогу обсуждения ни один синтаксис не оказался лучше шаблона while True с if not условие: break.

Что быстрее — for или while?
Для перебора коллекции for обычно быстрее и короче: ему не нужен ручной счетчик и проверка условия на каждом шаге. Для точной оценки на своих данных используйте модуль timeit.

Можно ли изменить переменную цикла for внутри тела?
Можно, но на следующей итерации она получит очередное значение из range или коллекции. Чтобы управлять шагом вручную, используйте while.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)