Зачем в Python нужен блок "finally"?
Я не совсем понимаю, зачем нужен блок finally
в конструкции try...except...finally
. На мой взгляд, вот этот код:
try:
run_code1()
except TypeError:
run_code2()
other_code()
товарищ с этим кодом, использующим finally
:
try:
run_code1()
except TypeError:
run_code2()
finally:
other_code()
Неужели эти два блока кода по сути одинаковы? Не упустил ли я что-то важное?
5 ответ(ов)
Да, ранний возврат действительно имеет значение. Рассмотрим ваш код подробнее.
В первом примере:
try:
run_code1()
except TypeError:
run_code2()
return None # Блок finally выполняется перед возвратом из метода
finally:
other_code()
Здесь блок finally
обязательно выполнится перед завершением метода, даже если возникнет исключение или будет выполнен возврат из метода. Это означает, что other_code()
всегда будет вызван, независимо от того, было ли исключение TypeError
или нет.
Теперь сравним это со вторым примером:
try:
run_code1()
except TypeError:
run_code2()
return None
other_code() # Этот код не выполнится, если возникнет исключение.
В этом случае, если run_code1()
вызовет исключение типа TypeError
, блок except
выполнится и произойдёт возврат из метода, а other_code()
не будет вызван. Таким образом, выполнение кода зависит от того, возвращаем ли мы раньше в блоке except
.
Кроме того, есть и другие ситуации, которые могут привести к различиям в поведении:
- Если исключение возникает внутри блока
except
, то это может повлиять на исполнение кода. - Если
run_code1()
вызывает исключение, но это неTypeError
, тогда код в блокеexcept
не выполнится иother_code()
также не будет вызван. - Кроме того, такие операторы управления потоком, как
continue
иbreak
, могут также повлиять на то, как и когда выполняется оставшийся код.
Таким образом, важно обращать внимание на структуру вашего кода и его поведение в случаях исключений для правильного управления потоком выполнения.
Чтобы дополнить другие ответы, стоит отметить, что блок finally
выполняется в любом случае, а блок else
выполняется только в том случае, если исключение не было вызвано.
Например, запись в файл без исключений будет выводить следующее:
file = open('test.txt', 'w')
try:
file.write("Testing.")
print("Запись в файл.")
except IOError:
print("Не удалось записать в файл.")
else:
print("Запись успешна.")
finally:
file.close()
print("Файл закрыт.")
ВЫВОД:
Запись в файл.
Запись успешна.
Файл закрыт.
Если возникает исключение, код выведет следующее (обратите внимание, что ошибка вызвана тем, что файл открыт только для чтения):
file = open('test.txt', 'r')
try:
file.write("Testing.")
print("Запись в файл.")
except IOError:
print("Не удалось записать в файл.")
else:
print("Запись успешна.")
finally:
file.close()
print("Файл закрыт.")
ВЫВОД:
Не удалось записать в файл.
Файл закрыт.
Мы можем видеть, что блок finally
выполняется независимо от наличия исключения. Надеюсь, это поможет.
Ваша программа демонстрирует работу конструкции try-except-finally
в Python. Чтобы прояснить разницу, вот что происходит в различных сценариях:
- Случай
a, b = 0, 1
:
try:
a/b
print('In try block')
except TypeError:
print('In except block')
finally:
print('In finally block')
print('Outside')
Вывод:
In try block
In finally block
Outside
В этом случае не возникает ошибок, поэтому блок except
пропускается, и программа продолжает выполнение, доходя до блока finally
, а затем выводит 'Outside'.
- Случай
a, b = 1, 0
:
try:
a/b
print('In try block')
except TypeError:
print('In except block')
finally:
print('In finally block')
print('Outside')
Вывод:
In finally block
Traceback (most recent call last):
a/b
ZeroDivisionError: division by zero
Здесь возникает ошибка деления на ноль. Поскольку нет блока except
, обрабатывающего ZeroDivisionError
, блок finally
выполняется, но программа затем завершает выполнение с ошибкой.
- Случай
a, b = 0, '1'
:
try:
a/b
print('In try block')
except TypeError:
print('In except block')
finally:
print('In finally block')
print('Outside')
Вывод:
In except block
In finally block
Outside
В этом примере возникает TypeError
, так как вы пытаетесь делить на строку. Исключение перехватывается в блоке except
, и программа продолжает выполнение, доходя до блока finally
, после чего выводит 'Outside'.
Примечание: Если у вас есть блок except
, обрабатывающий все типы ошибок, то блок finally
может оказаться избыточным, так как все ошибки будут обработаны в блоке except
.
Кодовые блоки не эквивалентны. В отличие от первого варианта, в котором other_code()
не будет выполнен, если run_code1()
выбрасывает исключение, кроме TypeError
, или если run_code2()
вызывает исключение, блок finally
будет выполнен в любом случае. Таким образом, разница заключается в том, что finally
выполняется в любом случае, даже если возникает другое исключение, в то время как в первом варианте это не так.
В вашем первом примере, что произойдет, если run_code1()
вызовет исключение, которое не является TypeError
? В этом случае other_code()
не будет выполнен.
Сравните это с версией с блоком finally:
: other_code()
будет гарантированно выполнен, независимо от того, будет ли вызвано какое-либо исключение.
Как проверить, существует ли переменная?
Как протестировать, что функция Python вызывает исключение?
Как зарегистрировать ошибку в Python с отладочной информацией?
Получить описание исключения и стек вызовов, вызвавших исключение, в виде строки
Переизбрасывание исключения в Python с сохранением трассировки стека