Это хорошая практика, чтобы использовать 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
676   9  

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

    Ничего не найдено.