Перейти к содержимому
Теория · Исключения · урок 4 из 11

else и finally

Представьте, что вы замешиваете тесто для пирога. Если тесто получилось, вы ставите его в духовку. Если не получилось, выбрасываете его. А миску моете в любом случае, что бы ни вышло.

У try есть ветки ровно для этих трёх случаев:

  • ветка except — «не получилось»;
  • ветка else — «получилось, продолжаем»;
  • ветка finally — «моем миску в любом случае».

Ветки else и finally пишутся после всех except, на одном уровне с try:

Python
def show_double(text):
    try:
        n = int(text)
    except ValueError:
        print("bad", end=" ")
    else:
        print(n * 2, end=" ")
    finally:
        print("checked")


show_double("5")  # 10 checked
show_double("x")  # bad checked

Давайте посмотрим, что происходит при каждом вызове.

Вызов show_double("5"):

  1. int("5") проходит, и n становится 5.
  2. Ветка except пропускается, а else печатает 10.
  3. Ветка finally печатает checked в той же строке.

Вызов show_double("x"):

  1. int("x") возбуждает ValueError, и блок try прерывается.
  2. Ветка except печатает bad, а else пропускается.
  3. Ветка finally снова печатает checked.

Ветка else

Ветка else выполняется, только если в блоке try не возникло исключения. Это слово уже встречалось у циклов, но здесь у него другой смысл: else у try значит «ошибки не было».

Тот же print(n * 2) можно было бы оставить внутри try. Но тогда except ValueError ловил бы ошибки и из этой строки, хотя защищать нужно только int(text). В else стоит код, ошибки которого этот try перехватывать не должен.

Запомните правило: в try кладут только ту строку, которая может упасть ожидаемым образом. Всё, что продолжает работу после её успеха, уходит в else.

Ветка finally

Ветка finally выполняется всегда. Когда именно:

  • после успешного try;
  • после сработавшего except;
  • даже когда исключение никто не перехватил — тогда Python сначала выполняет finally, а потом печатает трассировку.

Поэтому в finally пишут завершающие действия, которые нельзя пропустить. Здесь аналогия с миской заканчивается: на кухне вы можете забыть её помыть, а Python ветку finally не забудет никогда.

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

Python
def half(text):
    try:
        return int(text) // 2
    finally:
        print("half of", text, end=": ")


print(half("9"))  # half of 9: 4

Разберём по шагам:

  1. Вызов half("9") вычисляет 4 в строке с return.
  2. Перед выходом из функции выполняется finally и печатает half of 9: .
  3. Только после этого внешний print получает 4 и печатает его в той же строке.

Вызов half("x") тоже напечатал бы half of x: , но затем программа остановилась бы с трассировкой: ValueError никто не перехватил.

На собеседовании любят спрашивать: «Выполнится ли finally, если в try стоит return?» Ответ — да, выполнится, и до того, как значение вернётся вызывающему коду.

Частая ошибка

В finally написали return:

Python
def to_int(text):
    try:
        return int(text)
    finally:
        return 0  # ✗


print(to_int("7"))  # 0
print(to_int("x"))  # 0

Python 3.12 не сообщает об ошибке. return в finally заменяет значение из try, поэтому to_int("7") возвращает 0. Он отменяет и исключение: ValueError из int("x") пропадает без трассировки. В finally оставляют только завершающие действия, а значение возвращают из try, except или else.

Главное

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

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом