Как стать автором
Обновить

Поймай меня, если сможешь: руководство по обработке исключений в Python

Уровень сложности Средний
Время на прочтение 8 мин
Количество просмотров 26K
Всего голосов 14: ↑14 и ↓0 +14
Комментарии 21

Комментарии 21

Логичнее была бы инструкция then, чем else.

Сколько статей не читаю про исключения почти везде пытаются использовать исключения как замену if. Тот же пример с делением на 0 приводится везде, но ведь там логичнее использовать if и код будет более понятным разве нет? Пока видел всего несколько мест где действительно требуются исключения.

1) Это если вы пишете системный код с которым работает другой код(всяческие библиотеки и прочее)

2) У вас глубоко вложенная логика работы программы когда множество функций вызываются внутри друг друга и чтобы не передавать результат работы одной функции сквозь все дерево вызовов проще кинуть исключение которое обработать в главном методе.

Но никто не приводит таких примеров в статье, все ограничиваются поверхностным объяснением и очень примитивными примерами в которых объясняется синтаксис исключений, но не суть.

Ну или вот вы хотите открыть и прочитать файл. Что может пойти не так? Да сразу много чего. Логично ли все эти возможные косяки проверять if-ами? Нет. Гораздо проще поймать исключение и там уже будет написана довольно локализованная проблема - такого файла нет или у вас нет прав его читать или файл лежит на сетевом диске, который отвалился, или ещё что-то. Ну и в целом да, вы никогда не знаете, какой уровень контроля окажется в итоге нужен при вызове метода. Вот обнаружили вы деление на 0 с помощью if - и что дальше то? При использовании исключений всё понятно - возникает исключение и летит наверх до того уровня, где его считают нужным поймать. И не нужно придумывать какие-то отдельные конвенции - что же вы должны делать, что возвращать, если у вас функция деления и вы обнаружили в знаменателе ноль.

Ну а что может пойти не так при работе с файлом?

Прав нет, вызвали метод проверки прав перед открытием файла показали сообщение пользователю что прав нет.

Файла нет, показали сообщение пользователю что файла нет.

Код ясный и понятный, нормальные человеческие сообщения пользователю. Или вы предлагаете пользователю отдавать вот это

"FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'"

Я предпочту написать нормально сообщение + залогировать что такой то пользователь пытался получить доступ к несуществующему файлу.

"Файл с именем 'file.txt' не найден. Обратитесь в тех поддержку -7(999)555-55-55."

Вы не в курсе, что можно ловить не общий Exception, а конкретные исключения типа того же FileNotFoundError и выдавать понятные пользователю сообщения в этом случае? Это всё-равно будет проще, чем проверять все косяки вручную, да и нет никакой гарантии, что, например, файл не удалят или не поменяют ему права между моментом, когда вы его проверите через if и моментом когда откроете файл в программе. И поэтому вам всё-равно придётся ловить исключения и как-то их обрабатывать.

В курсе. Предлагаете все конкретные исключения в except'e отлавливать?

Я вот предпочту вообще до исключения не доводить. Ну и зачем пытаться читать файл с диска если прав нет. В том смысле что зачем пытаться выполнить код, если заранее известно что он упадет с исключением?

Я не говорю что исключения совсем не нужны, я пытаюсь сказать что каждому инструменту свое время и место. В большинстве случаев лучше использовать if чем пытаться отовсюду ловить исключения.

То есть вы предлагаете программистам делать двойную работу? )) Сначала проверять что-то через if, а потом ещё ловить соответствующий exception. Прелестно, прелестно )) И да, хороший стиль - именно ловить в блоке try-except все конкретные exception, которые вы ожидаете, что вылетят и вы знаете, как вы хотите на них реагировать. А те, с которыми не знаете что делать - не ловить, пусть на них выше по стеку кто-то реагирует. Это в любом руководстве написано.

Вы видимо совсем не читаете мои сообщения, либо не хотите понять. Я нигде не говорил что нужно делать двойную работу. Нужно грамотно организовывать код, где-то сделать проверку if где то обработку исключения. Но не нужно подменять одно другим.

Я всё прекрасно понимаю. У вас какой опыт вообще в программировании, сколько лет и какого грейда?
Я же уже приводил пример, что между проверкой на всякое и открытием файла его статус может успеть поменяться и вам всё-равно придётся проверять те исключения, которых вы пытаетесь избежать.

Опыт лет 20, грейд хз на бейджике написано "Ведущий инженер-программист".

Ну то есть фактически senior. Ну, все за карьеру могут с разными ситуациями сталкиваться, может у вас не бывало таких задачек, где критично сделать всё идеально или около того.

Разрабатываю систему в которой около 1млн активных пользователей. Все задачки где критично сделать идеально или около того.)

Во-первых, даже если есть права, файл может быть занят. Или файл уже открыт, но пользователь выдернул флэшку/отключил соединение с сетевым диском. Часто бывает так, что не попытавшись выполнить операцию - не поймёшь, выполнима ли она.

Во-вторых, в интервал времени между проверкой и открытием файла может произойти много всякого. Вон, был пример где службу обновления Steam обманывали, подсунув свой код, потому что она сначала открывала файл для проверки подписи, а потом закрывала и переоткрывала для выполнения.

Так что от неожиданных проблем никуда не деться. Стоит ли городить отдельный огород для проблем ожидаемых, в таком случае?

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

Конечно, есть разные подходы, на тех же плюсах, гугл пишет с отключенными исключениями Но это тоже палка о двух концах, если вам нужна надежная система на плюсах, в которой точно не текут ресурсы или другие неожиданные проблемы, то проще писать без исключений. Если же просто надо красиво завершится с дампом и стэк трейсом, или понятным сообщением, то проще писать с исключениями.

Так я пишу что для ситуации глубокой вложенности кода проще кинуть исключение и ловить его выше, чем морочится с ифами. Вот только никто такого примера не приводит в статьях про исключения.

Может тут найдётся что-то полезное LBYL vs EAFP

Вот это хороший обзор двух подходов, да.

Что то подобное я и пытался сказать)

Встречал оба подхода — if и exception.
Выбор подхода — вопрос философии разработки или корпоративных стандартов.


В своих проектах использую:


  • if во всех случаях, когда возможна ошибка
  • класс aggregated_parameter, который включает класс messages, в котором есть список errors для if ошибок.
  • в ключевых местах кода проверяю aggregated_parameter.messages.errors.count и выхожу, с вежливым объяснением ошибки, которая возвращается пользователю (aggregated_parameter.messages), без кишок stack trace.
    В реальности, возвращается пользователю errors, warnings и information сообщения.
    Например, information нужен когда в работе бизнес логики выполнена задача, которую необходимо озвучить пользователю для прозрачности процессов.

Автор, похоже, писал примеры без понимания того, как реально работают исключения. В частности в примере с открытием файла клауза finally написано просто неправильно. Если open() сгенерировала ошибку, то переменная file вообще не будет определена. В результате будет сгенерировано ещё одно исключение NameError. Уж лучше читайте учебники, чем блоггеров с Medium.

Важно отличать подходы EAFP(Проще просить прощения, чем разрешения) и LBYL(Смотри, прежде чем прыгать). LBYL стоит применять когда ты работаешь со статическими данными, например: обработка результата функции, проверка ключа в словаре и т.д. EAFP используется когда работа ведется с динамически изменяемой средой, например: проверка доступности удаленного сервера не дает тебе гарантию, что сервер не сломается сразу после ответа на проверку, или файл не будет удален после открытия в приложении и т.д.

Хороший пример EAFP метод raise_for_status библиотеки requests, который поднимает ошибку, а не генерирует ответ с результатом валидации статуса ответа.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий