Каковы некоторые изящные способы абстрагирования повторяющейся обработки исключений в python?
При обработке исключений в python я часто повторяю код. Основной шаблон имеет вид:
try:
action_here()
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()
Что я хотел бы сделать, так это то, как абстрактный этот повторяющийся код вызывается функцией или классом. Я знаю, что один способ сделать это - вызвать функцию обработки исключений с объектом исключения, например:
try:
action_here()
except Exception as e:
handle_exception(e)
Затем в этой функции определите исключение, основанное на классе.
def handle_exception(e):
if type(e) == type(CommonException1()):
Action_always_taken_for_CommonException1()
elif type(e) == type(CommonException2()):
Action_always_taken_for_CommonException2())
else:
Default_action_always_taken()
Это, однако, кажется неуклюжим и неуклюжим. Итак, мой вопрос в том, каковы другие альтернативы обработке повторяющихся исключений?
Ответы
Ответ 1
Эта ситуация является одним из основных вариантов использования для менеджеров контекста и оператора with:
from __future__ import with_statement # Needed in 2.5, but not in 2.6 or later
from contextlib import contextmanager
@contextmanager
def handle_exceptions():
try:
yield # Body of the with statement effectively runs here
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()
# Used as follows
with handle_exceptions():
action_here()
Ответ 2
Если вам не нравятся повторяющиеся блоки if
/elseif
, вы можете поместить свои дескрипторы в dict, с ключом по типу:
handlers = { type(CommonException1()) : Action_always_taken_forCommonException1,
type(CommonException2()) : Action_always_taken_forCommonException2 }
def handle_exception(te):
if te in handlers:
handlers[te]()
else:
Default_action()
Что вы могли бы запустить с помощью:
try:
action_here()
except Exception as e:
handle_exception(type(e))
Кроме того: Если вы часто пишете эти блоки try, тогда вы можете написать свой собственный менеджер контекстов (см. здесь). На стороне action_here()
ваш код будет выглядеть следующим образом:
with my_error_handling_context():
action_here1()
action_here2()
В этом случае код handle_exception
по существу будет вашим менеджером контекста __exit__
(который всегда будет передавать любые исключения, возникающие во время блока with).
Ответ 3
Хотя решение с использованием диспетчера контекста (как было предложено другими) является самым элегантным, и я бы тоже рекомендовал его, я хотел бы указать, что ваша функция handle_exception
может быть написана более элегантно с помощью re - Исключение исключения:
def handle_exception(e):
try:
raise e
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()