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; иначе — сообщи, что товара нет.
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. Вытряхнем коробку до дна и поищем в ней ключ:
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 внутри тела цикла. Код запускается без ошибок, но печатает не то:
goods = ['сыр', 'хлеб']
for g in goods:
if g == 'икра':
print('Нашли икру')
break
else:
print('Не икра:', g)
Не икра: сыр Не икра: хлеб
Напечатались обе строки «Не икра» — потому что этот else принадлежит if и срабатывает на каждой итерации, где условие ложно. Перенесите else на уровень for — и блок станет цикловым: сработает один раз после перебора. Отступ здесь не украшение, а переключатель смысла.
continue не отменяет else
Правило про else держится на одном условии: ни одного break за весь перебор. Continue тут ни при чём — он пропускает шаг, но цикл продолжается, а значит, до конца перебор доходит и ветка «ничего не нашли» остаётся в силе.
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 в глубине тела тихо его выключит, и никто этого не заметит.
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)
Один и тот же скелет подходит поиску по корзине, по файлу, по базе данных. Для сложных поисков — «первый подходящий элемент с условием» — есть и продвинутые приёмы, им посвящён урок семнадцатый. А поведение 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('делителей нет')
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 по словарю, если искомого ключа нет?
В списке оценок grades = [3, 5, 4, 2, 5] найдите двойку через for-else: если двойка есть — напечатайте Пересдача и выйдите через break, иначе — напечатайте Всё сдано.
Что делает 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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Циклы · Урок 6
Оператор break в Python: выход из цикла
break выходит из цикла немедленно: нашли нужное — перебирать остальное незачем. Разбираем break в for и while, поиск первого совпадения и мёртвый код после выхода.
Циклы · Урок 7
Оператор continue в Python: пропуск итерации
continue пропускает остаток тела для текущего элемента и переходит к следующему: не выход из цикла, а фильтр в потоке. Сравниваем с break на одних данных.
Циклы · Урок 17
Паттерн «первый подходящий»: поиск в цикле Python
Три способа найти первый подходящий элемент: цикл с break, next с генератором и ветка «не нашлось» — с дефолтом вместо падения.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Исключения · Урок 16
Ошибки в цикле Python: try внутри for и продолжение после сбоя
Сто строк данных, одна битая — и без защиты падает весь проход. Ставим try вокруг одного элемента: сбой обрабатывается, цикл идёт дальше, сбои считаются и попадают в журнал.
python ошибки в циклеpython продолжить цикл после ошибки
Множества · Урок 5
in в Python: мгновенная проверка принадлежности множеству
Один оператор, два механизма: in у множества отвечает мгновенно, у списка — перебором. Почему на миллионе элементов разница становится днями и секундами.
python проверка принадлежностимножество python поиск
Циклы · Урок 4
enumerate в Python: индекс и значение вместе
Нужен номер элемента вместе с самим элементом? enumerate отдаёт циклу пары «индекс — значение»: без ручного счётчика и с нумерацией с единицы через start=1.
нумерация в цикле pythonpython номер элемента в списке