Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 9 из 20 Начальный 15 мин 100 XP

else у циклов в Python: поиск без флагов

У циклов for и while есть собственный else: он срабатывает, если перебор дошёл до конца без break. Поиск «нашли или нет» — в один заход и без флагов.

Редакция Питоники

Найти в списке и отреагировать — у любого поиска две развилки: что делать, когда нашли, и что делать, когда не нашли. Классическое решение идёт через переменную-флаг. У Python есть путь короче: у циклов for и while есть собственный else. Разбираемся, когда он срабатывает, почему считается самым неожиданным блоком языка — и почему для поиска он очень удобен.

Поиск с флагом: как пишут без else

Вот типичный поиск товара по списку — так его пишут на многих языках:

поиск через флаг
goods = ['сыр', 'хлеб', 'масло']

found = False
for g in goods:
    if g == 'сыр':
        found = True

if found:
    print('Сыр есть')
else:
    print('Сыра нет')
Вывод
Сыр есть

Схема работает: флаг found со стартовым False, if внутри цикла поднимает его, проверка после цикла. Но три сущности ради одной проверки — флаг, подъём флага, проверка флага. И флаг коварен: во втором поиске его легко забыть сбросить в False, и результат первого протечёт во второй.

else у for: срабатывает без break

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

тот же поиск через for-else
goods = ['сыр', 'хлеб', 'масло']

for g in goods:
    if g == 'сыр':
        print('Нашли:', g)
        break
else:
    print('Такого товара нет')
Вывод
Нашли: сыр

Сыр нашёлся на первой же итерации — break оборвал перебор, и else не сработал. Поменяем запрос на икру — и картина перевернётся:

промах поиска
goods = ['сыр', 'хлеб', 'масло']

for g in goods:
    if g == 'икра':
        print('Нашли:', g)
        break
else:
    print('Икры нет, берём хлеб')
Вывод
Икры нет, берём хлеб

Икры в списке нет: перебор дошёл до конца, break так и не случился — тогда и сработал else. Найдено и не найдено обрабатываются в одном заходе, без флага и без второй проверки после цикла. Точную формулировку стоит запомнить: else у цикла срабатывает тогда и только тогда, когда ни одна итерация не дошла до break.

Из этой формулировки следует неожиданное: ноль итераций тоже подходит. Пустой список — break взять неоткуда, цикл завершился сам, и else честно отрабатывает:

пустой список
todo = []
for task in todo:
    print('Делаю:', task)
else:
    print('Дел нет — можно гулять')
Вывод
Дел нет — можно гулять

else у while

У while блок else работает по тому же правилу: срабатывает, если условие стало ложным само, а не через break. Вытряхнем коробку до дна и поищем в ней ключ:

while-else: коробка без ключа
box = ['мел', 'бумага', 'скотч']

while box:
    item = box.pop()
    if item == 'ключ':
        print('Ключ нашёлся')
        break
else:
    print('Коробка пуста: ключа не было')
Вывод
Коробка пуста: ключа не было

pop() достаёт элементы с конца, пока коробка не опустеет; условие while box стало ложным само — цикл завершился без break, значит else сработал. Оборвите перебор break-ом — и else пролетит мимо, как в примере с сыром.

Поиск по словарю

Словарь — обычная последовательность для цикла, и for-else работает с ним без всяких поправок. Это удобно, когда искомое приходит от пользователя и может отсутствовать: нашли — сообщаем, не нашли — тоже сообщаем, и оба исхода живут в одном заходе.

поиск блюда в меню
menu = {'хлеб': 42, 'сыр': 590}
want = 'сыр'
for name, price in menu.items():
    if name == want:
        print('нашли', name, price)
        break
else:
    print('нет такого блюда')
Вывод
нашли сыр 590

Сыр нашёлся на втором шаге, break прервал перебор, и else не сработал. Поменяйте want на 'компот' — и сообщение о промахе появится само, без отдельной проверки и без флага.

Ловушка: else прилип к if

Самая частая ошибка с этой конструкцией — else с отступом от if внутри тела цикла. Код запускается без ошибок, но печатает не то:

else прилип к if, а не к циклу
goods = ['сыр', 'хлеб']
for g in goods:
    if g == 'икра':
        print('Нашли икру')
        break
    else:
        print('Не икра:', g)
Вывод
Не икра: сыр
Не икра: хлеб

Напечатались обе строки «Не икра» — потому что этот else принадлежит if и срабатывает на каждой итерации, где условие ложно. Перенесите else на уровень for — и блок станет цикловым: сработает один раз после перебора. Отступ здесь не украшение, а переключатель смысла.

continue не отменяет else

Правило про else держится на одном условии: ни одного break за весь перебор. Continue тут ни при чём — он пропускает шаг, но цикл продолжается, а значит, до конца перебор доходит и ветка «ничего не нашли» остаётся в силе.

continue не выключает else
count = 0
while count < 3:
    count = count + 1
    if count == 2:
        continue
    print('шаг', count)
else:
    print('счётчик дошёл до', count)
Вывод
шаг 1
шаг 3
счётчик дошёл до 3

Двойка была пропущена через continue, но цикл дошёл до конца, break не случался — и else отработал. Обратите внимание на счётчик: он увеличился и на пропущенном шаге, потому что строка count = count + 1 стоит выше continue. Именно так это и должно быть — порядок строк в while не случаен.

Когда else у цикла мешает

Конструкция не всегда к месту. Если цикл занимается не поиском, а обычной работой — копит сумму, считает элементы, — блок else выглядит как случайный кусок кода без смысла. И хуже того: лишний break в глубине тела тихо его выключит, и никто этого не заметит.

else, который никогда не сработает
data = [3, 5, 7]
total = 0
for n in data:
    if n == 5:
        break
    total = total + n
else:
    print('перебор без break, сумма', total)
print('готово')
Вывод
готово

Напечаталось только «готово». Цикл дошёл до пяти и вышел через break — блок else пропущен, хотя по замыслу он должен был сообщить сумму. Итог молча неверен: ни суммы, ни объяснения, почему её нет.

Классика: проверка простого числа

Самый известный пример for-else — простые числа. Делим число на всё подряд от двух до n-1: нашёлся делитель — составное, выходим; перебор закончился без делителей — простое.

простое или составное
n = 13
for d in range(2, n):
    if n % d == 0:
        print(n, '- составное')
        break
else:
    print(n, '- простое')
Вывод
13 - простое

else здесь читается по-русски: делителей не нашлось. Без этой конструкции пришлось бы заводить флаг is_prime, поднимать его в цикле и проверять после — ровно та схема с флагом, от которой мы ушли в начале урока.

Поиск по запросу пользователя

В настоящих программах искомое приходит от человека: строка из формы, слово из командной строки. Шаблон тот же — цикл ищет, break фиксирует находку, else отвечает за пустой результат:

поиск по корзине
basket = ['сыр', 'хлеб', 'масло']

query = input('Что ищем? ')
for item in basket:
    if item == query:
        print('Есть в корзине')
        break
else:
    print('В корзине нет:', query)
Блок с input() в песочнице не запустится — ввода там нет. Локально это рабочий шаблон: введите, например, сыр или икра.

Один и тот же скелет подходит поиску по корзине, по файлу, по базе данных. Для сложных поисков — «первый подходящий элемент с условием» — есть и продвинутые приёмы, им посвящён урок семнадцатый. А поведение break, от которого зависит else, разобрано в шестом уроке.

Флагfor-else
Переменные до циклаодна-две (found)ни одной
Проверка после цикланужнане нужна — else сам
Главный рискфлаг забыли сброситьслучайный break глушит else
Читаетсяпривычнее новичкуидиоматичнее для Python
Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

items = [1, 3, 5]
for x in items:
    if x == 3:
        print('Нашли')
        break
else:
    print('Нет')
items = [2, 4]
for x in items:
    if x == 3:
        break
else:
    print('Троек нет')
print('Конец')
n = 9
for d in range(2, n):
    if n % d == 0:
        print('делитель', d)
        break
else:
    print('делителей нет')
Проверь себя
0 / 8

1. Когда срабатывает else у цикла for?

2. Что произойдёт с блоком else, если break сработал на первой итерации?

3. Список пуст, цикл for по нему закончился сразу. Сработает ли else?

4. else написан с отступом от if внутри тела цикла. Что это значит?

5. continue сработал внутри цикла, break ни разу не случился. Что с else?

6. Сработал ли else у цикла, если внутри был continue, но ни одного break?

7. Почему else у цикла опасен в цикле, который копит сумму?

8. Что напечатает for-else по словарю, если искомого ключа нет?

Карточки терминов
Запомнено: 0 / 8
Практика

В списке оценок grades = [3, 5, 4, 2, 5] найдите двойку через for-else: если двойка есть — напечатайте Пересдача и выйдите через break, иначе — напечатайте Всё сдано.

practice.py
Вопросы и ответы по уроку

Что делает else у цикла for в Python?

Это блок, который выполняется после цикла, если тот завершился без break. Читается как «если перебор дошёл до конца». Самое частое применение — поиск: break при находке, else — сообщение о промахе, без переменной-флага.

Урок 6: break

Почему else у цикла считают непонятной конструкцией?

Потому что слово else уже занято условным оператором, и по привычке ждут связки с if. У цикла смысл другой: «если не случился break». Ловушка с отступом усиливает путаницу: else на уровне if внутри цикла работает на каждой итерации, а на уровне for — один раз после перебора.

Чем заменить for-else, если конструкция не читается?

Классикой с флагом: found = False до цикла, подъём внутри, проверка после. Или встроенными проверками any и all — «хотя бы один» и «все», а для первого подходящего элемента — next с генератором.

Урок 17: паттерн первый подходящий

Как понять, на уровне кого стоит else — цикла или if?

По отступу: else на том же уровне, что заголовок for или while, — это else цикла; else глубже, на уровне if, — часть условного оператора. Ошибка отступа здесь не падает, а молча меняет смысл — проверяйте уровни внимательно.

Работает ли else у цикла с continue внутри?

Да. У else одно условие: за весь перебор не случилось ни одного break. Continue шаг пропускает, но цикл не обрывает, поэтому блок else отработает как обычно.

Урок 7: continue — пропуск итерации

Понравился урок? Сошлитесь на него

«else у цикла срабатывает тогда и только тогда, когда ни одна итерация не дошла до break.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.