Это хорошая практика, чтобы использовать try-except-else в Python?
время от времени в Python, я вижу блок:
try:
try_this(whatever)
except SomeException as exception:
#Handle exception
else:
return something
в чем причина существования попытки-кроме-другого?
мне не нравится такой вид программирования, так как он использует исключения для выполнения управления потоком. Однако, если он включен в язык, для этого должна быть веская причина, не так ли?
насколько я понимаю, исключения не являются ошибками, и что они должны использоваться только в исключительных условия (например, я пытаюсь записать файл на диск, и больше нет места, или, может быть, у меня нет разрешения), а не для управления потоком.
обычно я обрабатываю исключения как:
something = some_default_value
try:
something = try_this(whatever)
except SomeException as exception:
#Handle exception
finally:
return something
или если я действительно не хочу ничего возвращать, если происходит исключение, то:
try:
something = try_this(whatever)
return something
except SomeException as exception:
#Handle exception
9 ответов:
Я не знаю, если это по незнанию, но мне это не нравится вид программирования, так как он использует исключения для выполнения управления потоком."
в мире Python использование исключений для управления потоком является обычным и нормальным.
даже разработчики ядра Python используют исключения для управления потоком, и этот стиль сильно запечен в языке (т. е. протокол итератора использует StopIteration сигнал петли окончание.)
кроме того, try-except-style используется для предотвращения условий гонки, присущих некоторым из "смотри-прежде-чем-прыгнешь" конструктов. Например, тестирование os.путь.существует приводит к информации, которая может быть устаревшей к моменту ее использования. Аналогично,очереди.полный возвращает информацию, которая может быть несвежим. Элемент try-except-else style произведет более надежный код внутри указанные случаи.
"Это мое понимание, что исключения не являются ошибками, они должны только используйте для исключительных условий"
в некоторых других языках это правило отражает их культурные нормы, отраженные в их библиотеках. "Правило" также частично основано на соображениях производительности для этих языков.
культурная норма питона несколько отличается. Во многих случаях, вы должны используйте исключения для управления потоком. Кроме того, использование исключений в Python не замедляет окружающий код и вызывающий код, как это происходит в некоторых скомпилированных языках (т. е. CPython уже реализует код для проверки исключений на каждом шаге, независимо от того, используете ли вы на самом деле исключения или нет).
"однако, если это включенный в сам язык, должен быть веская причина, не так ли?"
кроме того, что помогает избежать условий гонки, исключения также очень полезны для вытягивания обработки ошибок за пределы циклов. Это необходимая оптимизация в интерпретируемых языках, которые, как правило, не имеют автоматического инвариант цикла код движения.
кроме того, исключения могут упростить код совсем немного в обычных ситуациях, когда способность обрабатывать проблему далека удалено оттуда, где возникла проблема. Например, обычно код пользовательского интерфейса верхнего уровня вызывает код бизнес-логики, который, в свою очередь, вызывает подпрограммы низкого уровня. Ситуации, возникающие в низкоуровневых подпрограммах (например, повторяющиеся записи для уникальных ключей в доступе к базе данных), могут обрабатываться только в коде верхнего уровня (например, запрашивать у пользователя новый ключ, который не конфликтует с существующими ключами). Использование исключений для этого вида потока управления позволяет подпрограммам среднего уровня полностью игнорируйте проблему и будьте хорошо отделены от этого аспекта управления потоком.
есть хороший пост в блоге о необходимости исключений здесь.
кроме того, см. Этот ответ переполнения стека:действительно ли исключения для исключительных ошибок?
", что является основанием для попробовать-кроме-еще существует?"
В остальном-статья сама по себе интересна. Он работает, когда нет исключения, но до напоследок-оговорка. В этом его главная цель.
без предложения else единственным вариантом запуска дополнительного кода до завершения была бы неуклюжая практика добавления кода в предложение try. Это неуклюже, потому что это рискует создание исключений в коде, который не был предназначен для защиты с помощью try-block.
прецедент запуска дополнительного незащищенного кода до завершения не возникает очень часто. Так что, не ожидайте увидеть много примеров в опубликованных код. Это довольно редкое явление.
другой вариант использования для предложения else-это выполнение действий, которые должны выполняться при отсутствии исключений и которые не выполняются при обработке исключений. Например:
recip = float('Inf') try: recip = 1 / f(x) except ZeroDivisionError: logging.info('Infinite result') else: logging.info('Finite result')наконец, наиболее распространенное использование предложения else в блоке try-для небольшого украшения (выравнивание исключительных результатов и не исключительных результатов на одном и том же уровне отступа). Это использование всегда является необязательным и не является строго необходимым.
в чем причина существования try-except-else?
A
tryблок позволяет обрабатывать ожидаемую ошибку. Элементexceptблок должен ловить только исключения, которые вы готовы обрабатывать. Если вы обрабатываете неожиданную ошибку, ваш код может сделать что-то не так и скрыть ошибки.An
elseпредложение будет выполняться, если не было ошибок, и не выполняя этот код вtryблок, вы избегаете ловить неожиданную ошибку. Снова, обнаружение неожиданной ошибки может скрыть ошибки.пример
например:
try: try_this(whatever) except SomeException as the_exception: handle(the_exception) else: return somethingв "try, except" suite есть два необязательных предложения,
elseиfinally. Так что это на самом делеtry-except-else-finally.
elseбудет оценивать только если нет исключения изtryзаблокировать. Это позволяет нам упростить более сложный код ниже:no_error = None try: try_this(whatever) no_error = True except SomeException as the_exception: handle(the_exception) if no_error: return somethingтак что если мы сравним
elseк альтернативе (которая может создать ошибки) мы смотрите, что это уменьшает строки кода, и мы можем иметь более читаемый, ремонтопригодный и менее глючный код-базу.
finally
finallyбудет выполняться независимо от того, что, даже если другая строка оценивается с помощью оператора return.сломанный с псевдо-кодом
это может помочь разбить это, в наименьшей возможной форме, которая демонстрирует все функции, с комментариями. Предположим, что это синтаксически правильно (но не выполняется если имена не определены) псевдокод находится в функции.
например:
try: try_this(whatever) except SomeException as the_exception: handle_SomeException(the_exception) # Handle a instance of SomeException or a subclass of it. except Exception as the_exception: generic_handle(the_exception) # Handle any other exception that inherits from Exception # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit # Avoid bare `except:` else: # there was no exception whatsoever return something() # if no exception, the "something()" gets evaluated, # but the return will not be executed due to the return in the # finally block below. finally: # this block will execute no matter what, even if no exception, # after "something" is eval'd but before that value is returned # but even if there is an exception. # a return here will hijack the return functionality. e.g.: return True # hijacks the return in the else clause aboveэто правда, что мы может добавить код
elseблокtryблок вместо этого, где он будет работать, если бы не было исключений, но что, если этот код сам вызывает исключение такого рода, которое мы ловим? Оставив его вtryблок скроет эту ошибку.мы хотим минимизировать строки кода в
tryзаблокировать, чтобы избежать перехват исключений мы не ожидали, по принципу, что если наш код терпит неудачу, мы хотим, чтобы он потерпел неудачу громко. Это же лучшие практики.насколько я понимаю, исключения не являются ошибками
в Python большинство исключений-это ошибки.
мы можем просмотреть иерархию исключений с помощью pydoc. Например, в Python 2:
$ python -m pydoc exceptionsили Python 3:
$ python -m pydoc builtinsдаст нам иерархия. Мы видим, что большинство видов
Exceptionошибки, хотя Python использует некоторые из них для таких вещей, как конечныеforпетли (StopIteration). Это иерархия Python 3:BaseException Exception ArithmeticError FloatingPointError OverflowError ZeroDivisionError AssertionError AttributeError BufferError EOFError ImportError ModuleNotFoundError LookupError IndexError KeyError MemoryError NameError UnboundLocalError OSError BlockingIOError ChildProcessError ConnectionError BrokenPipeError ConnectionAbortedError ConnectionRefusedError ConnectionResetError FileExistsError FileNotFoundError InterruptedError IsADirectoryError NotADirectoryError PermissionError ProcessLookupError TimeoutError ReferenceError RuntimeError NotImplementedError RecursionError StopAsyncIteration StopIteration SyntaxError IndentationError TabError SystemError TypeError ValueError UnicodeError UnicodeDecodeError UnicodeEncodeError UnicodeTranslateError Warning BytesWarning DeprecationWarning FutureWarning ImportWarning PendingDeprecationWarning ResourceWarning RuntimeWarning SyntaxWarning UnicodeWarning UserWarning GeneratorExit KeyboardInterrupt SystemExitкомментатор спросил:
скажем, у вас есть метод, который пингует внешний API, и вы хотите обработать исключение в классе вне оболочки API, вы просто возвращаете e из метода в соответствии с предложением except, где e является исключением возражаете?
нет, вы не возвращаете исключение, просто повторите его с голым
raiseчтобы сохранить stacktrace.try: try_this(whatever) except SomeException as the_exception: handle(the_exception) raiseили, в Python 3, Вы можете создать новое исключение и сохранить backtrace с цепочкой исключений:
try: try_this(whatever) except SomeException as the_exception: handle(the_exception) raise DifferentException from the_exceptionя уточнил в мой ответ здесь.
Python не подписывается на идею, что исключения должны использоваться только в исключительных случаях, на самом деле идиома "просите прощения, а не разрешения". Это означает, что использование исключений в качестве рутинной части управления потоком вполне приемлемо и фактически поощряется.
это вообще хорошо, так как работа таким образом помогает избежать некоторых проблем (как очевидный пример, условия гонки часто избегаются), и он имеет тенденцию делать код немного больше читаемый.
представьте себе, что у вас есть ситуация, когда вы берете некоторый пользовательский ввод, который должен быть обработан, но по умолчанию уже обработан. Элемент
try: ... except: ... else: ...структура делает для очень читабельный код:try: raw_value = int(input()) except ValueError: value = some_processed_value else: # no error occured value = process_value(raw_value)сравнить с тем, как это может работать на других языках:
raw_value = input() if valid_number(raw_value): value = process_value(int(raw_value)) else: value = some_processed_valueобратите внимание на преимущества. Нет необходимости проверять допустимость значения и разбирать его отдельно, они выполняются один раз. Код также следует более логичной прогрессии, основной путь кода сначала следует "Если это не сработает, сделайте это".
пример конечно, немного надуманный, но он показывает, что есть случаи для этой структуры.
это хорошая практика, чтобы использовать try-except-else в python?
ответ на это, что это зависит от контекста. Если вы сделаете это:
d = dict() try: item = d['item'] except KeyError: item = 'default'это показывает, что вы не очень хорошо знаете язык Python. Эта функциональность инкапсулирована в
dict.getспособ:item = d.get('item', 'default')The
try/exceptблок-это гораздо более визуально загроможденный и подробный способ написания того, что может быть эффективно выполнено в одной строке с помощью атомарного метода. Есть и другие случаи, когда это верно.однако это не означает, что мы должны избегать обработки всех исключений. В некоторых случаях предпочтительно избегать условий гонки. Не проверяйте, существует ли файл, просто попробуйте открыть его и поймать соответствующий IOError. Для простоты и удобочитаемости, попробуйте инкапсулировать это или разложить его как уместно.
читать Дзен питона, понимая, что есть принципы они находятся в напряжении, и будьте осторожны с догмой, которая слишком сильно зависит от любого из утверждений в ней.
вы должны быть осторожны с использованием блока finally, так как это не то же самое, что использовать блок else в try, за исключением. Блок finally будет запущен независимо от результата try except.
In [10]: dict_ = {"a": 1} In [11]: try: ....: dict_["b"] ....: except KeyError: ....: pass ....: finally: ....: print "something" ....: somethingКак все отметили, использование блока else делает ваш код более читаемым и запускается только тогда, когда исключение не выбрасывается
In [14]: try: dict_["b"] except KeyError: pass else: print "something" ....:
всякий раз, когда вы видите это:
try: y = 1 / x except ZeroDivisionError: pass else: return yили даже так:
try: return 1 / x except ZeroDivisionError: return Noneвместо того, чтобы рассмотреть этот:
import contextlib with contextlib.suppress(ZeroDivisionError): return 1 / x
Это мой простой фрагмент о том, как понять try-except-else-finally block В Python:
def div(a, b): try: a/b except ZeroDivisionError: print("Zero Division Error detected") else: print("No Zero Division Error") finally: print("Finally the division of %d/%d is done" % (a, b))давайте попробуем div 1/1:
div(1, 1) No Zero Division Error Finally the division of 1/1 is doneдавайте попробуем div 1/0
div(1, 0) Zero Division Error detected Finally the division of 1/0 is done
см. следующий пример, который иллюстрирует все о try-except-else-finally:
for i in range(3): try: y = 1 / i except ZeroDivisionError: print(f"\ti = {i}") print("\tError report: ZeroDivisionError") else: print(f"\ti = {i}") print(f"\tNo error report and y equals {y}") finally: print("Try block is run.")реализовать его и найти:
i = 0 Error report: ZeroDivisionError Try block is run. i = 1 No error report and y equals 1.0 Try block is run. i = 2 No error report and y equals 0.5 Try block is run.
OP, ВЫ ПРАВЫ. остальное после try / except в Python уродливо. это приводит к другому объекту управления потоком, где он не нужен:
try: x = blah() except: print "failed at blah()" else: print "just succeeded with blah"совершенно ясный эквивалент:
try: x = blah() print "just succeeded with blah" except: print "failed at blah()"это гораздо яснее, чем предложение else. Остальное после try / except не часто пишется, поэтому требуется время, чтобы понять, каковы последствия.
только потому, что вы можете сделать что-то, не означает, что вы должны сделать вещь.
многие функции были добавлены к языкам, потому что кто-то думал, что это может пригодиться. Проблема в том, что чем больше функций, тем менее ясные и очевидные вещи, потому что люди обычно не используют эти колокола и свистки.
просто мои 5 копеек. Я должен прийти и очистить много кода, написанного 1-м годом из разработчиков колледжа, которые думают, что они умны и хотят писать код в каком-то убер-плотном, Убер-эффективном способе, когда это просто делает его беспорядок, чтобы попытаться прочитать / изменить позже. Я голосую за читаемость каждый день и дважды по воскресеньям.
Comments