Я работаю над книгой «Head First Python» (это мой язык для изучения в этом году), и я попал в раздел, где они спорят о двух методах кода:
Проверка First против Exception обработка.
Вот пример кода Python:
# Checking First
for eachLine in open("../../data/sketch.txt"):
if eachLine.find(":") != -1:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
# Exception handling
for eachLine in open("../../data/sketch.txt"):
try:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
except:
pass
Первый пример имеет дело непосредственно с проблемой в .split
функции. Второй просто позволяет обработчику исключений справиться с этим (и игнорирует проблему).
В книге утверждается, что вместо обработки сначала используется обработка исключений. Аргумент заключается в том, что код исключения будет перехватывать все ошибки, тогда как проверка в первую очередь будет отлавливать только то, о чем вы думаете (и вы пропустите угловые случаи). Меня научили проверять в первую очередь, поэтому мой первоначальный инстинкт должен был это сделать, но их идея интересна. Я никогда не думал об использовании обработки исключений для рассмотрения дел.
Что из двух обычно считается лучшей практикой?
источник
if -1 == eachLine.find(":"): continue
тогда остаток цикла также не будет отступать.Ответы:
В .NET принято избегать чрезмерного использования Исключений. Одним из аргументов является производительность: в .NET создание исключения обходится вычислительно дорого.
Другая причина избежать их чрезмерного использования заключается в том, что может быть очень трудно читать код, который слишком сильно зависит от них. Запись в блоге Джоэла Спольски хорошо описывает проблему.
В основе аргумента лежит следующая цитата:
Лично я выкидываю исключения, когда мой код не может делать то, что по контракту. Я склонен использовать try / catch, когда собираюсь иметь дело с чем-то за пределами моей границы процесса, например, вызовом SOAP, вызовом базы данных, файловым вводом-выводом или системным вызовом. В противном случае я пытаюсь защищать код. Это не жесткое и быстрое правило, но это общая практика.
Скотт Хансельман также пишет об исключениях в .NET здесь . В этой статье он описывает несколько правил, касающихся исключений. Мой любимый?
источник
В частности, в Python обычно лучше ловить исключение. Как правило, его называют « Проще просить прощения, чем разрешения» (EAFP) по сравнению с «Look Before You Leap» (LBYL). Есть случаи, когда LBYL в некоторых случаях выдаст вам небольшие ошибки .
Однако, будьте осторожны голыми
except:
заявления , а также за бортом , за исключением заявлений, так как они оба могут также маскировать ошибки - то , как это было бы лучше:источник
try
/,except
а не установки условного выражения для «есть вероятность, что следующее вызовет исключение», и практика Python определенно предпочтительнее первого. Не повредит, что этот подход (возможно) более эффективен, поскольку в случае успешного разбиения вы можете пройти строку только один раз. Что касается того,split
должно ли здесь генерироваться исключение, я бы сказал, что оно определенно должно - одно общее правило: вы должны бросать, когда вы не можете делать то, что говорит ваше имя, и вы не можете разделить пропущенный разделитель.Прагматичный подход
Вы должны быть оборонительными, но до определенной степени. Вы должны написать обработку исключений, но в точку. Я собираюсь использовать веб-программирование в качестве примера, потому что здесь я живу.
Это из опыта работы в больших командных сценариях.
Аналогия
Представьте, если вы все время носите скафандр внутри МКС. Было бы трудно пойти в ванную или поесть вообще. Было бы очень громоздко внутри космического модуля перемещаться. Это было бы отстой. Написание кучки пробных отловов внутри вашего кода вроде как. У вас должен быть какой-то момент, когда вы говорите: эй, я обеспечил МКС, и с моими астронавтами все в порядке, так что просто не практично носить скафандр для каждого сценария, который может случиться.
источник
Основной аргумент книги заключается в том, что исключительная версия кода лучше, потому что она поймает все, что вы могли упустить из виду, если попытались написать свою собственную проверку ошибок.
Я думаю, что это утверждение верно только в очень специфических обстоятельствах - где вам все равно, если вывод правильный.
Нет сомнений в том, что поднятие исключений является надежной и безопасной практикой. Вы должны делать это всякий раз, когда чувствуете, что в текущем состоянии программы есть что-то, с чем вы (как разработчик) не можете или не хотите иметь дело.
Ваш пример, однако, о ловле исключений. Если вы поймаете исключение, вы не защитите себя от сценариев, которые вы могли упустить из виду. Вы делаете с точностью до наоборот: вы предполагаете, что вы не пропустили ни одного сценария, который мог бы вызвать исключение такого типа, и поэтому вы уверены, что его можно перехватить (и, таким образом, не дать программе выйти из программы, как и любое необъяснимое исключение).
Используя подход исключения, если вы видите
ValueError
исключение, вы пропускаете строку. Используя традиционный подход без исключений, вы подсчитываете количество возвращаемых значенийsplit
, а если оно меньше 2, вы пропускаете строку. Должны ли вы чувствовать себя более защищенно с подходом исключения, так как вы могли забыть о некоторых других ситуациях "ошибки" в своей традиционной проверке ошибок, иexcept ValueError
поймали бы их для вас?Это зависит от характера вашей программы.
Если вы пишете, например, веб-браузер или видеоплеер, проблема со входами не должна приводить к его аварийному завершению с неперехваченным исключением. Гораздо лучше вывести что-то отдаленно разумное (даже если, строго говоря, неправильно), чем выйти.
Если вы пишете приложение, в котором важна корректность (например, деловое или инженерное программное обеспечение), это будет ужасный подход. Если вы забыли о каком-то сценарии, который возникает
ValueError
, худшее, что вы можете сделать, это молча проигнорировать этот неизвестный сценарий и просто пропустить строку. Вот как очень тонкие и дорогостоящие ошибки заканчиваются в программном обеспечении.Вы можете подумать, что единственный способ, которым вы можете видеть
ValueError
в этом коде, - этоsplit
возвращать только одно значение (вместо двух). Но что, если вашеprint
утверждение позже начнет использовать выражение, которое возникаетValueError
при некоторых условиях? Это заставит вас пропустить некоторые строки не потому, что они пропустили:
, а потому, чтоprint
на них не получилось . Это пример тонкой ошибки, о которой я говорил ранее - вы ничего не заметите, просто потеряете несколько строк.Я рекомендую избегать перехвата (но не возбуждения!) Исключений в коде, где неправильный вывод хуже, чем выход. Единственный раз, когда я ловлю исключение в таком коде, это когда у меня действительно тривиальное выражение, поэтому я легко могу понять, что может вызывать каждый из возможных типов исключений.
Что касается влияния использования исключений на производительность, то оно тривиально (в Python), если исключения не встречаются часто.
Если вы используете исключения для обработки обычно возникающих условий, в некоторых случаях вы можете заплатить огромные затраты производительности. Например, предположим, что вы дистанционно выполняете какую-то команду. Вы можете проверить, что текст вашей команды проходит, по крайней мере, минимальную проверку (например, синтаксис). Или вы можете подождать, пока возникнет исключение (что происходит только после того, как удаленный сервер проанализирует вашу команду и обнаружит с ней проблему). Очевидно, что первый на несколько порядков быстрее. Другой простой пример: вы можете проверить, является ли число нулем ~ в 10 раз быстрее, чем пытаться выполнить деление, а затем перехватить исключение ZeroDivisionError.
Эти соображения имеют значение только в том случае, если вы часто отправляете искаженные строки команд на удаленные серверы или получаете аргументы с нулевым значением, которые вы используете для деления.
Примечание: я полагаю, вы бы использовали
except ValueError
вместо простоexcept
; как указывали другие, и как сказано в самой книге на нескольких страницах, вы никогда не должны использовать голыеexcept
.Еще одно примечание: правильный подход без исключения заключается в подсчете количества возвращаемых значений
split
, а не в поиске:
. Последний слишком медленный, поскольку повторяет выполненную работуsplit
и может почти удвоить время выполнения.источник
Как правило, если вы знаете, что оператор может генерировать недопустимый результат, проверьте его и разберитесь с ним. Используйте исключения для вещей, которые вы не ожидаете; материал, который является "исключительным". Это делает код более понятным в договорном смысле (например, «не должно быть нулевым»).
источник
Используйте то, что всегда хорошо работает в ..
Обработка исключений и защитное программирование - это разные способы выражения одного и того же намерения.
источник
TBH, не имеет значения, используете ли вы
try/except
механика илиif
проверку выписки. Обычно вы видите и EAFP, и LBYL в большинстве базовых версий Python, причем EAFP встречается немного чаще. Иногда ЭСПЦ является гораздо более удобным для чтения / идиоматическое, но в данном случае я думаю , что это хорошо в любом случае.Тем не мение...
Я буду осторожен, используя вашу текущую ссылку. Пара вопиющих проблем с их кодом:
with
идиому при чтении файлов в Python: исключений очень мало. Это не один из них.except
выражение, которое не перехватывает конкретное исключение)role, lineSpoken = eachLine.split(":",1)
Ivc имеет хороший ответ по этому поводу и EAFP, но также утечка дескриптор.
Версия LBYL не обязательно является такой же производительной, как версия EAFP, поэтому утверждение о том, что создание исключений «дорого с точки зрения производительности», категорически неверно. Это действительно зависит от типа строк, которые вы обрабатываете:
источник
В основном обработка исключений должна быть более подходящей для языков ООП.
Второй момент - производительность, потому что вам не нужно выполнять
eachLine.find
для каждой строки.источник
Я думаю, что защитное программирование вредит производительности. Вам также следует перехватывать только те исключения, которые вы собираетесь обработать, чтобы среда выполнения работала с исключением, которое вы не знаете, как обрабатывать.
источник