Python: исключение catch внутри класса
Можно ли написать обработчик исключений, чтобы уловить ошибки во время выполнения, сгенерированные всеми методами класса? Я могу сделать это, окружив каждого из них try/except:
class MyError(Exception):
def __init__(self, obj, method):
print 'Debug info:', repr(obj.data), method.__name__
raise
class MyClass:
def __init__(self, data):
self.data = data
def f1(self):
try:
all method code here, maybe failing at run time
except:
raise MyError(self, self.f1)
Интересно, существует ли более общий способ добиться того же - для любой ошибки, возникающей в любом месте класса. Я хотел бы иметь доступ к данным класса для печати некоторой информации об отладке.
Кроме того, как мне получить имя метода отказа (f1 в примере)?
Обновление: благодаря всем, что касается безупречных ответов, идея декоратора выглядит как способ.
О риске захвата ВСЕХ исключений: инструкция raise
в ветке except
должна повторно поднимать исключение, не теряя никакой информации, не так ли? Вот почему я положил его и в MyError...
Ответы
Ответ 1
Предупреждение: если вы хотите что-то вроде этого, скорее всего, вы этого не сделаете... но если вы действительно хотите...
Что-то вроде:
import functools
def catch_exception(f):
@functools.wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
print 'Caught an exception in', f.__name__
return func
class Test(object):
def __init__(self, val):
self.val = val
@catch_exception
def calc():
return self.val / 0
t = Test(3)
t.calc()
показывает, как украсить отдельные функции. Затем вы можете создать декоратор класса для применения этого декоратора к каждому методу (будьте осторожны classmethod's
/staticmethod's
/properties
и т.д.)
Ответ 2
Предполагая, что у вас есть декоратор catch_exception
, как в ответе @Jon Clement...
class ErrorCatcher(type):
def __new__(cls, name, bases, dct):
for m in dct:
if hasattr(dct[m], '__call__'):
dct[m] = catch_exception(dct[m])
return type.__new__(cls, name, bases, dct)
class Test(object):
__metaclass__ = ErrorCatcher
def __init__(self, val):
self.val = val
def calc(self):
return self.val / 0
Метакласс применяет catch_exception
ко всему, что кажется методом, пока он определяет Test
.
В ответ на комментарий, касающийся пользовательских сообщений для каждого метода, в качестве атрибута можно добавить такое сообщение (или даже функцию обратного вызова для генерации сообщения):
class Test(object):
__metaclass__ = ErrorCatcher
def __init__(self, val):
self.val = val
def calc(self):
return self.val / 0
calc.msg = "Dividing by 0 is ill-advised"
Декоратор catch_exception
будет искать атрибут msg
в своем аргументе и использовать его, если он найден, при обработке исключения.
Этот подход может быть расширен; вместо строки msg
может быть отображением типов исключений для строк. В любом случае, строка может быть заменена (с поддержкой от catch_exception
, конечно) с помощью произвольной функции обратного вызова, которая принимает, скажем, повышенное исключение в качестве аргумента.
def calc_handler(exc):
# ...
calc.callback = calc_handler
Ответ 3
Декоратор будет хорошим решением здесь.
Вот пример того, как вы могли это сделать:
import inspect
def catch_exception_decorator(function):
def decorated_function:
try:
function()
except:
raise MyError(self.__class__, inspect.stack()[1][3])
return decorated_function
class MyClass(object):
def __init__(self):
...
@catch_exception_decorator
def f1(self):
...
@catch_exception_decorator поверх функции является ярлыком для f1 = catch_exception_decorator(f1)
.
Вместо того, чтобы делать self. class, вы также можете получить доступ к данным класса из экземпляра, если вы не затеняете переменные. inspect.stack() [1] [3] - имя функции текущей функции. Вы можете использовать их для создания атрибутов исключений.