5

Хорошая ли практика использовать try-except-else в Python?

25

У меня возник вопрос о конструкции try-except-else в Python. Иногда я сталкиваюсь с таким кодом:

try:
    try_this(whatever)
except SomeException as exception:
    # Обработка исключения
else:
    return something

В чем причина существования блока try-except-else?

Я не люблю такой подход в программировании, поскольку использование исключений для управления потоком исполнения кажется мне нецелесообразным. Если такая конструкция присутствует в языке, значит, на то должна быть веская причина, не так ли?

Я понимаю, что исключения — это не ошибки, и их следует использовать только для исключительных ситуаций (например, если я пытаюсь записать файл на диск, но на нем не осталось места, или если у меня нет разрешения), а не для управления потоком.

Обычно я обрабатываю исключения следующим образом:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    # Обработка исключения
finally:
    return something

Или, если я действительно не хочу возвращать ничего в случае возникновения исключения, то использую:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    # Обработка исключения

Буду признателен за разъяснения по поводу использования try-except-else в Python.

5 ответ(ов)

0

Ваш вопрос касается блока try-except-else-finally в Python, и вы привели отличный пример для его иллюстрации. Давайте разберёмся, как это работает.

Вот ваш код с комментариями:

for i in range(3):  # Проходим по значениям i от 0 до 2
    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.

Объяснение:

  1. Первая итерация (i = 0):

    • try блок вызывает исключение ZeroDivisionError, так как деление на 0 невозможно.
    • Управление переходит в except, где выводится сообщение об ошибке.
    • finally блок выполняется, выводя "Try block is run."
  2. Вторая итерация (i = 1):

    • Деление выполняется успешно, и переменная y получает значение 1.0.
    • Управление переходит в else, где выводится значение y.
    • finally блок снова выполняется.
  3. Третья итерация (i = 2):

    • Деление выполняется, y становится равным 0.5.
    • Во else блоке выводится это значение.
    • finally блок выполняется.

Таким образом, блоки try, except, else и finally позволяют гибко обрабатывать ошибки и гарантировать выполнение определённых действий независимо от того, произошла ошибка или нет. Надеюсь, это проясняет вашу ситуацию! Если у вас есть дополнительные вопросы, не стесняйтесь задавать их.

0

При использовании блока finally необходимо быть осторожным, так как он работает иначе, чем блок else в конструкции try...except. Блок finally выполняется независимо от результата выполнения блока try...except.

Вот пример:

dict_ = {"a": 1}

try:
    dict_["b"]
except KeyError:
    pass
finally:
    print("something")

В этом случае, даже если ключа "b" нет в словаре и возникает исключение KeyError, блок finally все равно выполнится и напечатает "something".

С другой стороны, использование блока else делает ваш код более читаемым, так как он выполняется только в том случае, если исключение не было выброшено:

try:
    dict_["b"]
except KeyError:
    pass
else:
    print("something")

В этом примере блок else не выполнится, так как при обращении к несуществующему ключу "b" возникнет исключение KeyError. Поэтому код внутри блока else не будет выполнен.

Таким образом, если вам нужно выполнить некоторый код только в случае успешного завершения блока try, использование блока else предпочтительнее, чем finally.

0

Хотя никто другой пока не высказывал такое мнение, я бы посоветовал

избегать использования else в блоках try/except, потому что это непонятно большинству людей.

В отличие от ключевых слов try, except и finally, смысл else не очевиден; это снижает читаемость. Поскольку else используется довольно редко, это может заставить других разработчиков, читающих ваш код, перепроверять документацию, чтобы убедиться, что они правильно понимают, что происходит.

Я пишу этот ответ именно потому, что наткнулся на try/except/else в своей кодовой базе, и это вызвало у меня недоумение, заставив меня погуглить.

Поэтому, когда я вижу код, напоминающий пример автора вопроса:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # выполняем дополнительные действия в случае отсутствия исключения
    return something

Я предпочел бы переписать его так:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# выполняем дополнительные действия в случае отсутствия исключения  <2>
return something
  • <1> Явный return четко показывает, что в случае исключения мы завершили выполнение.
  • <2> Как приятный побочный эффект, код, который раньше находился в блоке else, теперь сдвинут на один уровень влево.
0

Когда вы видите такой код:

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

Использование contextlib.suppress позволяет более элегантно обрабатывать исключения, устраняя необходимость в ручном использовании блока try/except. Это делает код более чистым, а его намерение более очевидным. Вы просто "гасите" конкретное исключение, и если оно возникает, выполнение продолжится без прерываний, что упрощает обработку ошибок в рамках данной конструкции.

0

Это мой простой пример, иллюстрирующий, как работает блок try-except-else-finally в Python:

def div(a, b):
    try:
        a / b
    except ZeroDivisionError:
        print("Обнаружена ошибка деления на ноль")
    else:
        print("Нет ошибки деления на ноль")
    finally:
        print("В конце деление %d/%d завершено" % (a, b))

Давайте попробуем выполнить деление 1/1:

div(1, 1)

Вывод будет следующим:

Нет ошибки деления на ноль
В конце деление 1/1 завершено

Теперь попробуем деление 1/0:

div(1, 0)

Вывод будет следующим:

Обнаружена ошибка деления на ноль
В конце деление 1/0 завершено

Таким образом, блок try пытается выполнить код внутри себя, и если возникает исключение (например, ZeroDivisionError), управление передается в блок except. Если исключения нет, выполняется блок else. В любом случае, блок finally будет выполнен в конце, независимо от того, произошло исключение или нет.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь