Работа с ошибками
Это плохая практика? На мой взгляд: Нет, это не так. В целом это ХОРОШАЯ практика:
def foo(a, b):
return a / b
def bar(a):
return foo(a, 0)
try:
bar(6)
except ZeroDivisionError:
print("Error!")
Причина проста: код, обрабатывающий ошибку, сконцентрирован в одной точке вашей основной программы.
В некоторых языках программирования исключения, которые потенциально могут быть вызваны, должны быть объявлены на уровне функций/методов. Python отличается: это язык сценариев, в котором отсутствуют подобные функции. Конечно, иногда вы можете неожиданно получить исключение, поскольку вы можете не знать, что другой код, который вы вызываете, может вызвать такое исключение. Но в этом нет ничего страшного: чтобы разрешить эту ситуацию, у вас есть try...except...
в вашей основной программе.
Вы можете компенсировать это отсутствие знаний о возможных исключениях следующим образом:
- документировать исключения, которые могут быть вызваны; если язык программирования здесь сам по себе не помогает, нужно восполнить этот недостаток, предоставив более обширную документацию;
- выполнить обширные тесты;
В общем нет смысла следовать вашему варианту б). Вещи могут быть более явными, но сам код не является подходящим местом для этой явной информации. Вместо этого эта информация должна быть частью документации вашей функции/метода.
Поэтому вместо...
def bar(a):
try:
ret = foo(a, 0)
except ZeroDivisionError:
raise
return ret
... записывать:
def bar(a):
"""
Might raise ZeroDivisionError
"""
return foo(a, 0)
Или как я бы написал:
#
# @throws ZeroDivisionError Does not work with zeros.
#
def bar(a):
return foo(a, 0)
(Но на какой именно синтаксис вы полагаетесь для документации, это совершенно другой вопрос и выходит за рамки этого вопроса.)
Бывают ситуации, когда перехват исключений внутри функции/метода является хорошей практикой. Например, это тот случай, когда вы хотите выполнить какой-либо метод успешно, даже если какая-то внутренняя операция может завершиться ошибкой. (Например, если вы пытаетесь прочитать файл, и если он не существует, вы хотите использовать данные по умолчанию.) Но перехватывать исключение только для того, чтобы вызвать его снова, как правило, не имеет никакого смысла: прямо сейчас я даже не могу придумать с ситуацией, когда это может быть полезно (хотя могут быть и некоторые особые случаи). Если вы хотите предоставить информацию о том, что такое исключение может быть вызвано, не полагайтесь на пользователей, изучающих реализацию, а скорее на документацию вашей функции/метода.
Вывод ошибок
В любом случае я бы не стал следовать вашему подходу, просто печатая простое сообщение об ошибке:
try:
bar(6)
except ZeroDivisionError:
print("Error!")
Довольно трудоемко придумать разумные, удобочитаемые и простые сообщения об ошибках. Раньше я делал это, но объем кода, необходимый для такого подхода, огромен. По моему опыту, лучше просто потерпеть неудачу и распечатать трассировку стека. С помощью этой трассировки стека обычно любой может очень легко найти причину ошибки.
К сожалению, Python не обеспечивает удобочитаемую трассировку стека в выводе ошибок. Чтобы компенсировать это, я реализовал свою собственную обработку вывода ошибок (повторно используемую в качестве модуля), которая даже использует цвета, но это другое дело и может также немного выходить за рамки этого вопроса.
person
Regis May
schedule
02.09.2020