Где обработать исключение
Исключения в Python — это особый объект, который умеет путешествовать между функциями и менять их поведение. Своим появлением исключение прерывает обычное исполнение программы — сверху вниз и вглубь — и переводит его в особый обратный режим — наверх до подходящего try except finally
.
Для примера рассмотрим программу — информер с прогнозом погоды. Функция request_weather
делает запрос к API сайта weather.com и возвращает прогноз погоды:
def request_weather():
response = requests.get('https://weather.com/api/weather/moscow/')
return response.json()
Если с сервером weather.com не удаётся связаться, то вызов метода requests.get
приводит к исключению ConnectionError
— к ошибке соединения:
def request_weather():
response = requests.get('https://weather.com/api/weather/moscow/')
return response.json()
request_weather()
# requests.exceptions.ConnectionError:
# ...
# Failed to establish a new connection: [Errno -2] Name or service not known',))
По логике программы при сбое на одном сайте мы тянем информацию с запасного сайта openweathermap.org:
def request_weather():
response = requests.get('https://weather.com/api/weather/moscow/')
return response.json()
def request_openweathermap():
response = requests.get('https://openweathermap.org/api/moscow/')
return response.json()
request_weather()
# TODO в случае сбоя вызывать `request_openweathermap`
Здесь начинаются сложности. Нельзя перехватывать исключение ConnectionError
внутри функции request_weather
, иначе внешний код не узнает о проблеме и не сможет переключиться на вызов второй функции request_openweathermap
. Перехватывать исключение нужно снаружи:
def request_weather():
response = requests.get('https://weather.com/api/weather/moscow/')
return response.json()
def request_openweathermap():
response = requests.get('https://openweathermap.org/api/moscow/')
return response.json()
try:
forecast = request_weather()
except requests.exceptions.ConnectionError:
forecast = request_openweathermap()
Исключение возникло внутри метода requests.get
и всплыло вверх — внутрь функции def request_weather
. Там не нашлось подходящего обработчика исключений, потому что не было конструкций try except
и поэтому исключение продолжило всплывать выше — наружу функции request_weather
. Подходящий обработчик нашелся в последних строках кода:
try:
forecast = request_weather()
except requests.exceptions.ConnectionError:
forecast = request_openweathermap()
Исключение всегда всплывает вверх по цепочке вызовов функций, пока его не перехватят. Если ни одна функция так и не обработает исключение, то вмешается сам интерпретатор Python: он перехватывает все что до него долетает и выводит на экран:
def request_weather():
response = requests.get('https://weather.com/api/weather/moscow/')
response.raise_for_status() # здесь возникло исключение
return response.json()
def request_openweathermap():
response = requests.get('https://openweathermap.org/api/moscow/')
response.raise_for_status()
return response.json()
def main():
try:
forecast = request_weather()
except requests.exceptions.ConnectionError:
forecast = request_openweathermap()
main() # здесь исключение будет перехвачено Питоном
# requests.exceptions.HTTPError: 404 Client Error: Not Found for url ...
# ...
В примере выше исключение HTTPError
возникло внутри метода response.raise_for_status()
, затем всплыло в функцию def request_weather():
и там его никто не обработал. Тогда исключение всплыло еще выше в функцию def main():
, но и там обработчика не нашлось. Исключение всплыло еще выше и оказалось на уровне интерпретатора Python — он перехватил исключение и вывел его на экран.
Где ловить исключения
Основная идея: исключение ловят не там, где оно возникло, а там где его удобно обработать. Исключение само будет всплывать от функции к функции пока не встретит подходящий обработчик, делать для этого ничего не надо, достаточно не мешать.
Алгоритм действий следующий. Сначала выкидываем исключение, чтобы сообщить об ошибке и прервать нормальное исполнение программы. Затем ищем место в коде, где это исключение удобнее всего обработать, и сразу придумываем логику обработки такого исключения. Если места не нашлось или обработка сводится к print(error)
, то отказываемся от перехвата исключения — интерпретатор Python в любом случае перехватывает все исключения и выводит их на экран.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.