Является ли хорошей практикой использовать 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-эффективном способе, когда это просто делает его беспорядок, чтобы попытаться прочитать / изменить позже. Я голосую за читаемость каждый день и дважды по воскресеньям.