Обобщенная обработка исключений в Python "Правильный путь"
Иногда я оказываюсь в ситуации, когда хочу выполнить несколько последовательных команд, таких как:
try:
foo(a, b)
except Exception, e:
baz(e)
try:
bar(c, d)
except Exception, e:
baz(e)
...
Такая же картина возникает, когда исключения просто необходимо игнорировать.
Это кажется излишним, а чрезмерный синтаксис делает его на удивление трудным для понимания при чтении кода.
В Си я бы легко решил этот тип проблемы с помощью макроса, но, к сожалению, это невозможно сделать на прямом питоне.
Вопрос: Как лучше всего уменьшить объем кода и повысить читаемость кода при встрече с этим шаблоном?
6 ответов
Вы могли бы использовать with
Скажите, если у вас есть Python 2.5
from __future__ import with_statement
import contextlib
@contextlib.contextmanager
def handler():
try:
yield
except Exception, e:
baz(e)
Ваш пример теперь становится:
with handler():
foo(a, b)
with handler():
bar(c, d)
Если это всегда, всегда поведение, которое вы хотите, когда конкретная функция вызывает исключение, вы можете использовать декоратор:
def handle_exception(handler):
def decorate(func):
def call_function(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception, e:
handler(e)
return call_function
return decorate
def baz(e):
print(e)
@handle_exception(baz)
def foo(a, b):
return a + b
@handle_exception(baz)
def bar(c, d):
return c.index(d)
Использование:
>>> foo(1, '2')
unsupported operand type(s) for +: 'int' and 'str'
>>> bar('steve', 'cheese')
substring not found
Если это простые однострочные команды, вы можете заключить их в lambda
s:
for cmd in [
(lambda: foo (a, b)),
(lambda: bar (c, d)),
]:
try:
cmd ()
except StandardError, e:
baz (e)
Вы можете обернуть все это в функцию, чтобы она выглядела так:
ignore_errors (baz, [
(lambda: foo (a, b)),
(lambda: bar (c, d)),
])
Лучший подход, который я нашел, состоит в том, чтобы определить такую функцию:
def handle_exception(function, reaction, *args, **kwargs):
try:
result = function(*args, **kwargs)
except Exception, e:
result = reaction(e)
return result
Но это просто не чувствуется или выглядит неправильно на практике:
handle_exception(foo, baz, a, b)
handle_exception(bar, baz, c, d)
Вы можете попробовать что-то вроде этого. Это неопределенно C-подобный макросу.
class TryOrBaz( object ):
def __init__( self, that ):
self.that= that
def __call__( self, *args ):
try:
return self.that( *args )
except Exception, e:
baz( e )
TryOrBaz( foo )( a, b )
TryOrBaz( bar )( c, d )
В вашем конкретном случае вы можете сделать это:
try:
foo(a, b)
bar(c, d)
except Exception, e:
baz(e)
Или вы можете поймать исключение на один шаг выше:
try:
foo_bar() # This function can throw at several places
except Exception, e:
baz(e)