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

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

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

else и finally: два хвоста try

Два хвоста try: else работает при успехе, finally — при любом исходе. Разбираем порядок try, except, else, finally и место каждого блока.

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

К конструкции try и except у Python есть два дополнения, которые делают её завершённой. else отвечает на вопрос «что делать при успехе», finally — «что делать при любом исходе». Оба блока часто пропускают в первых программах, и зря: без else успешный код живёт в try и рискует попадать под обработчик, а без finally некуда поставить финальную уборку. Разберём оба хвоста по очереди.

else: если не упало

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

успех — работает else
try:
    n = int('42')
except ValueError:
    print('Не число')
else:
    print('Разобрали:', n)
Вывод
Разобрали: 42

Разбор прошёл — выполнение перешло в else, где переменная n уже существует и готова к работе. А теперь тот же каркас со значением, которое не разбирается:

авария — else пропущен
try:
    n = int('сорок два')
except ValueError:
    print('Не число')
else:
    print('Разобрали:', n)
Вывод
Не число

ValueError уронил try — выполнение ушло в except, а else пропустили целиком. Получается чётное разделение: except работает при аварии, else при успехе, и оба не выполняются вместе. Финальные строки после конструкции выполняются при любом исходе — но код, который осмыслен только после успешного разбора, правильнее держать в else.

Зачем else, если есть try

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

лишний код в try ломает вердикт
text = '18'
try:
    n = int(text)
    print('Разобрали:', n)
    print('Длина текста:', len(texts))
except ValueError:
    print('Не число')
Вывод
Разобрали: 18
Traceback (most recent call last):
  File "app.py", line 5, in <module>
    print('Длина текста:', len(texts))
                               ^^^^^
NameError: name 'texts' is not defined. Did you mean: 'text'?
Программа падает: опечатка texts вместо text случилась уже после успешного разбора, но лежала в try. Except ValueError её не поймал — и это правильно: ошибка не про разбор значения, а Python даже подсказал в сообщении правильное имя.

Разбор успел пройти, первая печать сработала — а опечатка в следующей строке упала мимо обработчика ValueError. Итог: половина блока выполнена, половина нет, и traceback указывает на строку, которая вообще не имела отношения к рискованной операции. Теперь тот же код с else — границы честные, try держит только разбор:

успешный код — в else
text = '18'
try:
    n = int(text)
except ValueError:
    print('Не число')
else:
    print('Разобрали:', n)
    print('Длина текста:', len(text))
Вывод
Разобрали: 18
Длина текста: 2

Теперь try — только разбор, а весь код, который осмыслен при успехе, живёт в else. Ошибка в else не попадает под except того же try: блоки равноправны, и опечатка здесь упала бы громко, как и положено багу. Правило статьи: в try — рискованная операция, в except — реакция на её аварию, в else — то, что имеет смысл только после успеха.

finally: всегда

Второй хвост — finally. Его обещание звучит безусловно: этот блок выполнится при любом исходе. Успех — выполнится, авария с обработкой — выполнится. Проверим на списке попыток:

finally при успехе и при аварии
for text in ['7', 'кот']:
    try:
        n = int(text)
        print('Разобрали:', n)
    except ValueError:
        print('Не число:', text)
    finally:
        print('--- попытка завершена')
Вывод
Разобрали: 7
--- попытка завершена
Не число: кот
--- попытка завершена

Первый проход прошёл через try и finally, второй — через except и всё равно через finally. Ни один шаг цикла не закончился без строки «попытка завершена» — в этом и смысл блока: он держит код, который обязан случиться. Классические жильцы finally: закрыть файл, отпустить соединение, засечь время, уменьшить счётчик попыток. Уборка не должна зависеть от того, как закончилась работа.

счётчик попыток в finally
attempts = ['12', 'x', '8']
good = 0
for text in attempts:
    try:
        n = int(text)
    except ValueError:
        print(text, ': не число')
    else:
        good += 1
        print(text, ': число', n)
    finally:
        print(text, ': попытка окончена')
print('Успешных попыток:', good)
Вывод
12 : число 12
12 : попытка окончена
x : не число
x : попытка окончена
8 : число 8
8 : попытка окончена
Успешных попыток: 2

Полный каркас из четырёх частей на одной программе: try держит разбор, except — вердикт о сбое, else — учёт успеха, finally — строку о завершении каждой попытки. Смотрите, как аккуратно разделились обязанности: каждое сообщение программы отвечает ровно за один факт.

finally даже при падении

Самое сильное обещание finally — выполнение даже тогда, когда исключение никто не поймал. Уберём обработчик и оставим только finally:

finally перед падением
print('до ошибки')
try:
    n = 10 / 0
finally:
    print('убираем за собой')
print('сюда не дойдём')
Вывод
до ошибки
убираем за собой
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    n = 10 / 0
        ~~~^~~
ZeroDivisionError: division by zero
Программа падает: обработчика нет. Но печать из finally успела выполниться до traceback — Python сначала отработал уборку, потом показал аварию.

Разберите вывод построчно: «до ошибки» — обычный код, «убираем за собой» — finally, и только затем traceback. Python не показал аварию, пока не выполнил обещание блока. Блок finally выполняется всегда: после except, после else и даже когда исключение никто не поймал. А строка «сюда не дойдём» так и осталась невыполненной — падение после finally всё-таки падение.

Порядок частей

Все четыре части собираются в жёстком порядке: try, затем один или несколько except, затем else, последним finally. Хвосты необязательны, но переставлять их нельзя — Python проверит структуру ещё до запуска:

else без except не бывает
try:
    x = 1
else:
    print(x)
Файл не запустится: Python ответит SyntaxError: expected 'except' or 'finally' block. Блок else имеет смысл только как хвост после обработчиков.

Зато finally может обходиться без единого except — мы видели это в прошлом блоке: try и finally законны сами по себе. Комбинации такие: try + except (минимум), try + finally (без обработки), полный набор try + except + else + finally.

полный набор в правильном порядке
try:
    n = int('5')
except ValueError:
    print('except')
else:
    print('else')
finally:
    print('finally')
Вывод
else
finally

Успех пропустил except и прошёл через else и finally — ровно в том порядке, в котором блоки стоят. Схема выполнения получается линейной: try, потом ровно одна ветка (except или else), потом finally.

БлокКогда выполняетсяЧто в него кладут
tryВсегда, первымТолько рискованную операцию
exceptПри исключении своего типаРеакцию на аварию
elseКогда try прошёл без исключенийКод, зависящий от успеха
finallyПри любом исходе, даже без обработчикаУборку: закрыть, отпустить, засечь

Четыре части try собраны и разложены по местам. Остался последний инструмент, который превращает обработку из оборонительной в наступательную: собственные исключения. В уроке про raise мы будем бросать ошибки сами — когда данные нарушают правила программы. А если хочется освежить базовую пару, она разобрана в уроке про try и except.

Что выведет код?

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

try:
    n = int('9')
except ValueError:
    print('А')
else:
    print('Б')
finally:
    print('В')
try:
    n = int('девять')
except ValueError:
    print('А')
else:
    print('Б')
finally:
    print('В')
for t in ['4', 'x']:
    try:
        n = int(t)
    except ValueError:
        print(t, 'сбой')
    else:
        print(t, 'успех')
    finally:
        print('---')
Проверь себя
0 / 8

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

2. Что гарантирует блок finally?

3. Зачем выносить успешный код из try в else?

4. Какой порядок частей правильный?

5. Законна ли конструкция try с finally без единого except?

6. Исключение никто не поймал, но в конструкции есть finally. Что напечатается?

7. Ошибки из блока else попадут под except того же try?

8. Куда положить закрытие файла, которое нужно и при успехе, и при сбое?

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

Дан список attempts = ['12', 'кот']. Для каждой строки: при успехе печатай «12 : число 12», при сбое — «кот : не число», а после каждой попытки — «<текст> : попытка окончена». Используй try, except, else и finally.

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

Что делает else в конструкции try except python?

Блок else выполняется, только если try прошёл без исключений. Туда кладут код, зависящий от успеха: печать результата, подсчёт, продолжение работы. При аварии else пропускается — работает except.

Урок 7: raise — бросаем исключение сами

Когда выполняется finally в python?

Всегда: при успехе после try, при аварии после except, и даже когда исключение никто не поймал — Python сначала выполнит finally, потом покажет traceback. Поэтому в finally ставят уборку: закрытие файла, освобождение ресурса.

Можно ли написать try и finally без except?

Да, это законная комбинация: try держит рискованный код, finally — безусловную уборку. А вот try с else без except не запускается: Python ответит SyntaxError: expected 'except' or 'finally' block.

Чем else отличается от кода после конструкции?

Строки после try/except выполняются при любом исходе. Else — только при успехе. Если код имеет смысл лишь после успешной операции (например, печатать n после разбора), его место в else — там он не выполнится с несуществующим n при аварии.

Почему успешный код не стоит держать в try?

Всё, что лежит в try, охраняется обработчиком: любая случайная ошибка там уходит в except или падает посреди блока. Границы читаются хуже, а вердикты врут. В try — только рискованная операция, в else — то, что осмысленно после успеха.

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

«Блок finally выполняется всегда: после except, после else и даже когда исключение никто не поймал.»

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

TelegramVK

Что дальше

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

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

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