Несколько except и else / finally
Несколько except и else / finally
В предыдущем уроке мы познакомились с базовым try/except. Python предлагает расширенный синтаксис для более тонкой обработки: блок else (выполняется когда исключений не было) и finally (выполняется всегда). Вместе они образуют полную конструкцию обработки ошибок.
Полный синтаксис
try:
# потенциально опасный код
pass
except ValueError:
# если произошёл ValueError
pass
except (KeyError, IndexError) as e:
# если произошёл KeyError или IndexError
pass
else:
# если try завершился БЕЗ исключений
pass
finally:
# выполняется ВСЕГДА: и при успехе, и при исключении
pass
Каждый блок необязателен, кроме одного except (или finally). Порядок строго такой: try → except* → else → finally.
Блок else: код успешного пути
else выполняется, только если в try не возникло исключений. Это позволяет чётко отделить «опасный код» от «кода при успехе»:
def load_config(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
data = f.read()
except FileNotFoundError:
print(f"Файл {filename} не найден, используем настройки по умолчанию")
return {}
else:
# сюда попадаем только если файл открылся успешно
import json
return json.loads(data)
config = load_config("settings.json")
Без else пришлось бы вложить json.loads(data) в блок try, рискуя перехватить json.JSONDecodeError там, где мы его не ожидали.
Блок finally: код очистки
finally выполняется всегда — независимо от того, возникло исключение или нет. Это идеальное место для освобождения ресурсов:
def process_file(filename):
f = None
try:
f = open(filename, "r", encoding="utf-8")
return f.read()
except FileNotFoundError:
return ""
finally:
if f is not None:
f.close() # закроем файл в любом случае
print("Файл закрыт")
На практике для файлов лучше использовать with open(...) — он делает то же самое через контекстный менеджер. Но finally незаменим для других ресурсов: соединений с базой, блокировок, временных файлов.
finally выполняется даже при return
Это важная тонкость: finally выполняется даже если в try или except встречается return:
def demo():
try:
return "из try"
finally:
print("finally всё равно выполнился")
result = demo()
# finally всё равно выполнился
print(result) # из try
Приоритет обработки исключений
Если определено несколько except, Python проверяет их сверху вниз и использует первый подходящий. Поэтому более конкретные исключения ставят выше:
try:
result = int(input("Число: "))
print(100 / result)
except ValueError:
print("Это не число")
except ZeroDivisionError:
print("Ноль недопустим")
except Exception as e:
print(f"Неожиданная ошибка: {e}")
Exception — базовый класс для большинства исключений. Если поставить его первым, он перехватит всё — конкретные except ниже никогда не сработают.
Практический пример: чтение и парсинг JSON
import json
def read_json(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
text = f.read()
except FileNotFoundError:
return None, "Файл не найден"
except PermissionError:
return None, "Нет прав на чтение"
else:
try:
data = json.loads(text)
except json.JSONDecodeError as e:
return None, f"Невалидный JSON: {e}"
else:
return data, None
finally:
print("Чтение завершено")
data, error = read_json("config.json")
if error:
print(f"Ошибка: {error}")
Итог
else— выполняется еслиtryзавершился без исключений; отделяет «опасный» код от «успешного».finally— выполняется всегда (успех или исключение); используется для очистки ресурсов.- Несколько
exceptпроверяются сверху вниз; конкретные — выше, общие — ниже. finallyвыполняется даже приreturnвнутриtry.
В следующем уроке разберём raise — как самостоятельно генерировать исключения в собственном коде.