«Я не знаю, происходит ли это по незнанию, но мне не нравится такой вид программирования, поскольку он использует исключения для управления потоком».
В мире Python использование исключений для управления потоком является обычным явлением.
Даже разработчики ядра Python используют исключения для управления потоком, и этот стиль в значительной степени встроен в язык (т. Е. Протокол итератора использует StopItered для завершения цикла сигнала).
Кроме того, стиль «попробуй кроме» используется для предотвращения условий гонки, присущих некоторым конструкциям «смотреть перед прыжком» . Например, тестирование os.path.exists приводит к получению информации, которая может устареть к тому времени, когда вы ее используете. Кроме того, Queue.full возвращает информацию, которая может быть устаревшей. В этом случае стиль try-исключением-else даст более надежный код.
«Если я понимаю, что исключения не являются ошибками, они должны использоваться только для исключительных условий»
В некоторых других языках это правило отражает их культурные нормы, отраженные в их библиотеках. «Правило» также частично основано на соображениях производительности для этих языков.
Культурная норма Python несколько иная. Во многих случаях вы должны использовать исключения для потока управления. Кроме того, использование исключений в Python не замедляет окружающий код и вызывающий код, как это происходит в некоторых скомпилированных языках (то есть CPython уже реализует код для проверки исключений на каждом шаге, независимо от того, используете ли вы на самом деле исключения или нет).
Другими словами, ваше понимание того, что «исключения являются исключительными» - это правило, которое имеет смысл в некоторых других языках, но не для Python.
«Однако, если он включен в сам язык, для этого должна быть веская причина, не так ли?»
Помимо того, что это помогает избежать условий гонки, исключения также очень полезны для обработки ошибок за пределами цикла. Это необходимая оптимизация в интерпретируемых языках, которые не склонны к автоматическому циклически-инвариантному движению кода .
Кроме того, исключения могут немного упростить код в обычных ситуациях, когда способность обрабатывать проблему далека от места возникновения проблемы. Например, обычно используется код пользовательского интерфейса верхнего уровня, вызывающий код для бизнес-логики, который, в свою очередь, вызывает процедуры низкого уровня. Ситуации, возникающие в процедурах низкого уровня (например, дубликаты записей для уникальных ключей при доступе к базе данных), могут обрабатываться только в коде верхнего уровня (например, при запросе у пользователя нового ключа, который не конфликтует с существующими ключами). Использование исключений для этого вида потока управления позволяет подпрограммам среднего уровня полностью игнорировать проблему и быть хорошо отделенным от этого аспекта управления потоком.
Здесь есть хорошая запись в блоге об обязательности исключений .
Кроме того, см. Ответ «Переполнение стека»: действительно ли исключения являются исключительными?
"Какова причина существования попытки, за исключением чего-либо еще?"
Само условие-это интересно. Он запускается, когда нет исключения, но до предложения finally. Это его основная цель.
Без предложения else единственным вариантом запуска дополнительного кода перед финализацией была бы неуклюжая практика добавления кода в предложение try. Это неуклюже, потому что это может привести к возникновению исключений в коде, который не предназначен для защиты блоком try.
Вариант использования дополнительного незащищенного кода перед финализацией возникает не очень часто. Так что не ожидайте увидеть много примеров в опубликованном коде. Это несколько редко.
Другим вариантом использования для предложения else является выполнение действий, которые должны происходить, когда не возникает исключение, и не происходят, когда обрабатываются исключения. Например:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Другой пример встречается у бегунов на юнит-тестах:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
И, наконец, наиболее распространенное использование выражения else в блоке try предназначено для небольшого улучшения (выравнивание исключительных результатов и неисключительных результатов при одинаковом уровне отступа). Это использование всегда необязательно и не является строго необходимым.
try
Блок позволяет обрабатывать ожидаемую ошибку.except
Блок должен поймать только исключения вы готовы к обработке. Если вы обрабатываете неожиданную ошибку, ваш код может сделать что-то не так и скрыть ошибки.Предложение
else
будет выполнено, если ошибок не было, и, не выполняя этот код вtry
блоке, вы избежите неожиданной ошибки. Опять же, обнаружение неожиданной ошибки может скрыть ошибки.пример
Например:
Пакет «try, кроме» содержит два необязательных предложения
else
иfinally
. Так оно и есть на самом делеtry-except-else-finally
.else
будет оценивать, только если нет исключения изtry
блока. Это позволяет нам упростить более сложный код ниже:поэтому, если мы сравним a
else
с альтернативой (которая может привести к ошибкам), мы увидим, что это сокращает количество строк кода, и мы можем получить более читабельную, поддерживаемую и менее ошибочную кодовую базу.finally
finally
будет выполняться независимо от того, что, даже если другая строка оценивается с оператором возврата.Разбит с помощью псевдокода
Это может помочь разобрать это в наименьшей возможной форме, которая демонстрирует все функции, с комментариями. Предположим, что этот синтаксически правильный (но не запускаемый, если не определены имена) псевдокод находится в функции.
Например:
Это правда, что мы могли бы вместо этого включить код в
else
блок вtry
блоке, где он будет работать, если бы не было исключений, но что, если сам этот код вызывает исключение того типа, который мы ловим? Оставив его вtry
блоке, можно было бы скрыть эту ошибку.Мы хотим минимизировать строки кода в
try
блоке, чтобы избежать перехвата исключений, которые мы не ожидали, по принципу, что если наш код дает сбой, мы хотим, чтобы он громко провалился. Это лучшая практика .В Python большинство исключений - ошибки.
Мы можем просмотреть иерархию исключений с помощью pydoc. Например, в Python 2:
или Python 3:
Даст нам иерархию. Мы можем видеть, что большинство видов
Exception
ошибок, хотя Python использует некоторые из них для таких вещей, как конечныеfor
циклы (StopIteration
). Это иерархия Python 3:Комментатор спросил:
Нет, вы не
raise
вернете исключение, просто создайте его заново, чтобы сохранить трассировку стека.Или в Python 3 вы можете вызвать новое исключение и сохранить обратный след с цепочкой исключений:
Я уточню в своем ответе здесь .
источник
raise
цепочки исключений или сделать цепочку исключений - но об этом подробнее и рассказано здесь: stackoverflow.com/q/2052390/541136 - я, вероятно, удалю эти комментарии после того, как видел, что ты видел их.Python не поддерживает идею, что исключения должны использоваться только в исключительных случаях, на самом деле идиома «просить прощения, а не разрешения» . Это означает, что использование исключений в качестве рутинной части управления потоком данных вполне приемлемо и, по сути, приветствуется.
Как правило, это хорошая вещь, поскольку работа таким образом помогает избежать некоторых проблем (как очевидный пример, часто избегаются условия гонки), и это делает код немного более читабельным.
Представьте, что у вас есть ситуация, когда вы берете некоторый пользовательский ввод, который должен быть обработан, но по умолчанию уже обрабатывается.
try: ... except: ... else: ...
Структура делает для очень читаемого кода:Сравните с тем, как это может работать на других языках:
Обратите внимание на преимущества. Нет необходимости проверять правильность значения и анализировать его отдельно, они выполняются один раз. Код также следует более логичной последовательности: сначала идет путь к основному коду, а затем «если это не работает, сделайте это».
Пример, естественно, немного надуманный, но он показывает, что есть случаи для этой структуры.
источник
Ответ заключается в том, что это зависит от контекста. Если вы делаете это:
Это показывает, что вы не очень хорошо знаете Python. Эта функциональность заключена в
dict.get
методе:Блок
try
/except
является гораздо более визуально загроможденным и многословным способом записи того, что может быть эффективно выполнено в одной строке атомарным методом. Есть и другие случаи, когда это правда.Однако это не означает, что мы должны избегать обработки всех исключений. В некоторых случаях предпочтительно избегать условий гонки. Не проверяйте, существует ли файл, просто попытайтесь открыть его и перехватите соответствующую ошибку IOError. Ради простоты и читабельности, попробуйте инкапсулировать это или вычленить как подходящее.
Прочитайте Zen of Python , понимая, что существуют принципы, которые находятся в напряжении, и будьте осторожны с догмой, которая слишком сильно зависит от любого из утверждений в нем.
источник
Посмотрите следующий пример, который иллюстрирует все о try-кроме-else-finally:
Реализуйте это и приходите:
источник
Вы должны быть осторожны с использованием блока finally, поскольку это не то же самое, что использование блока else в try, кроме. Блок finally будет выполняться независимо от результата попытки, кроме.
Как уже отмечалось, использование блока else делает ваш код более читабельным и запускается только тогда, когда исключение не выдается.
источник
Всякий раз, когда вы видите это:
Или даже это:
Рассмотрим это вместо этого:
источник
Просто потому, что никто не опубликовал это мнение, я бы сказал,
В отличие от ключевых слов
try
,except
иfinally
, значениеelse
пункта не самоочевидно; это менее читабельно. Поскольку он используется не очень часто, люди, читающие ваш код, захотят перепроверить документы, чтобы убедиться, что они понимают, что происходит.(Я пишу этот ответ именно потому, что я нашел
try/except/else
в своей кодовой базе, и это вызвало момент wtf и вынудило меня заняться поиском).Поэтому везде, где я вижу код, подобный примеру OP:
Я бы предпочел рефакторинг
<1> явный возврат, ясно показывает, что в исключительном случае мы закончили работу
<2> как приятный незначительный побочный эффект, код, который был в
else
блоке, отступает на один уровень.источник
Это мой простой фрагмент о том, как понять блок try-исключением-else-finally в Python:
Давайте попробуем div 1/1:
Давайте попробуем div 1/0
источник
ОП, ВЫ ПРАВИЛЬНО. Остальное после try / кроме в Python ужасно . это приводит к другому объекту управления потоком, где ни один не нужен:
Совершенно понятный эквивалент:
Это гораздо понятнее, чем предложение else. Иное после try / исключением пишется не часто, поэтому требуется время, чтобы понять, что это значит.
То, что вы МОЖЕТЕ делать что-то, не означает, что вы ДОЛЖНЫ делать что-то.
Множество функций было добавлено в языки, потому что кто-то думал, что это может пригодиться. Проблема в том, что чем больше функций, тем менее ясными и очевидными являются вещи, потому что люди обычно не используют эти навороты.
Просто мои 5 центов здесь. Я должен прийти и почистить большую часть кода, написанного первокурсниками из колледжских разработчиков, которые думают, что они умны и хотят писать код каким-то сверхурочным и сверхэффективным способом, когда это просто делает беспорядок попробовать и прочитать / изменить позже. Я голосую за удобочитаемость каждый день и дважды по воскресеньям.
источник
print
утверждение. Что произойдет, еслиx = blah()
вернет astr
, но ваш оператор печатиprint 'just succeeded with blah. x == %d' % x
? Теперь у вас естьTypeError
существо, созданное там, где вы не готовы с ним справиться; вы проверяете,x = blah()
чтобы найти источник исключения, а его там даже нет. Я делал это (или эквивалент) не раз, когдаelse
бы я не допустил эту ошибку. Теперь я знаю лучше. :-Delse
не является красивым утверждением, и пока вы не привыкли к нему, оно не интуитивно понятно. Но тогда, ни то, ни другое,finally
когда я впервые начал использовать его ...else
предложении не перехватываютсяexcept
.x = blah()
чтобы найти источник исключения», имеяtraceback
почему вы должны проверять источник исключения из неправильного места?