Зачем в 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 вызывает исключение?
Правильный способ использования try/except с модулем requests в Python?
Переизбрасывание исключения в Python с сохранением трассировки стека
Есть ли разница между поднятием экземпляра класса Exception и самого класса Exception?