Comments 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
. Ну, все за карьеру могут с разными ситуациями сталкиваться, может у вас не бывало таких задачек, где критично сделать всё идеально или около того.
Во-первых, даже если есть права, файл может быть занят. Или файл уже открыт, но пользователь выдернул флэшку/отключил соединение с сетевым диском. Часто бывает так, что не попытавшись выполнить операцию - не поймёшь, выполнима ли она.
Во-вторых, в интервал времени между проверкой и открытием файла может произойти много всякого. Вон, был пример где службу обновления 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, который поднимает ошибку, а не генерирует ответ с результатом валидации статуса ответа.
Поймай меня, если сможешь: руководство по обработке исключений в Python