Является ли хорошей практикой использовать 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 ответов
в чем причина того, что попытка-кроме-другого-существует?
A try
блок позволяет обрабатывать ожидаемую ошибку. The except
блок должен ловить только исключения, которые вы готовы обработать. Если вы обрабатываете непредвиденную ошибку, ваш код может сделать что-то не так и скрыть ошибки.
An else
предложение будет выполняться, если не было ошибок, и не выполняя этот код в try
блок, вы избегаете ловить непредвиденную ошибку. Снова, обнаружение неожиданной ошибки может скрыть ошибки.
пример
например:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
return something
в наборе "try, except" есть два необязательных предложения,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.
сломанный с псевдо-кодом
это может помочь разбить это, в наименьшей возможной форме, которая демонстрирует все функции, с комментариями. Предположим, что это синтаксически правильно (но не runnable если имена не определены) псевдо-код находится в функции.
например:
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 использует некоторые из них для таких вещей, как ending 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 не подписывается на идею о том, что исключения должны использоваться только в исключительных случаях, на самом деле идиома "просите прощения, а не разрешения". Это означает, что использование исключений в качестве рутинной части управления потоком вполне приемлемо и фактически поощряется.
это, как правило, хорошо, так как работа таким образом помогает избежать некоторых проблем (как очевидный пример, условия гонки часто избегаются), и это, как правило, делает код немного больше читаемый.
представьте, что у вас есть ситуация, когда вы принимаете некоторый пользовательский ввод, который должен быть обработан, но имеет значение по умолчанию, которое уже обработано. The 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')
на try
/except
block-это гораздо более визуально загроможденный и подробный способ написания того, что может быть эффективно выполнено в одной строке атомным методом. Есть и другие случаи, когда это верно.
однако это не означает, что мы должны избегать обработки всех исключений. В некоторых случаях предпочтительно избегать расовых условий. Не проверяйте, существует ли файл, просто попробуйте открыть его и поймать соответствующий IOError. Для простоты и удобочитаемости попробуйте инкапсулировать это или разложить на множители как уместное.
читать Дзен питона, понимая, что есть принципы они находятся в напряжении и остерегаются догмы, которая слишком сильно полагается на любое из содержащихся в ней утверждений.
вы должны быть осторожны с использованием блока finally, так как это не то же самое, что использовать блок else в try, кроме. Блок finally будет запущен независимо от результата попытки за исключением.
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-го года из разработчиков колледжа, которые думают, что они умны и хотят писать код в каком-то uber-плотном, uber-эффективном способе, когда это просто делает его беспорядок, чтобы попытаться прочитать / изменить позже. Я голосую за читаемость каждый день и дважды по воскресеньям.