Вопрос по exception-handling, python – Поймать исключения внутри класса

14

Можно ли написать обработчик исключений для перехвата ошибок времени выполнения, генерируемых ВСЕМИ методами в классе? Я могу сделать это, окружив каждого с помощью try / кроме:

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 в примере)?

UpdateСпасибо всем за глубокие ответы, идея декоратора выглядит как путь. О риске перехвата ВСЕХ исключений:raise заявление вexcept ветвь должна повторно вызвать исключение, не теряя никакой информации, не так ли? Вот почему я также поместил его в MyError ...

Вы могли бы написать декоратор для этого. phg
Вы также можете написать метакласс, который будет применять декоратор @ phg к каждому методу, когда он определен. chepner
@chepner Отправить это как ответ. Marcin
За класс исключений дляall исключения дляall методы в классе кажутся ошибкой проектирования. Так что еслиf1() получает IOError, это должно сообщить как MyError? Это неизбежно приводит к потере информации и путанице. Увидетьblog.ianbicking.org/2007/09/12/re-raising-exceptions для деталей. msw
возможный дубликат"outsourcing" exception-handling to a decorator Marcin

Ваш Ответ

3   ответа
13

catch_exception как в ответе Джона Клемента ...

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
У меня есть лучшие ответы, с которыми я здесь сталкиваюсь, это ТАК. Есть ли способ настроить сообщение об исключении для каждого метода?
+1 - хороший пример__metaclass__ - Если ОП собирался в конечном итоге выстрелить себе в ногу, по крайней мере, между нами мы предоставили примеры, чтобы убедиться, что они взорвали его сразу ...cough
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).

Вместо того, чтобы заниматься собой.classВы также можете получить доступ к данным класса из экземпляра, если только вы не используете теневые переменные. inspect.stack () [1] [3] - это имя функции текущей функции. Вы можете использовать их для создания атрибутов исключений.

17

Warning: if you want something like this, it's likely you don't... but if you really want to...

Что-то вроде:

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 так далее...)

@phg Я вижу, куда вы идете с этим - надеюсь, вы не возражаете, но я оставлю это для ОП.
+1 за "Вероятно, вы действительно не хотите что-то подобное".
Не могли бы вы объяснить, "вы действительно не хотите что-то подобное"? пожалуйста? У меня есть много сценариев, содержащих много классов, каждый со многими методами. Я хотел бы знать, какой класс / метод потерпел неудачу, без написания попытки / за исключением каждого
+1 полезно для демонстрацииalone, но я хочу еще раз подчеркнуть, что это действительно плохая плохая идея ...
Сделать этоsomehow более элегантно, я бы добавилhandler параметр, который используется внутриexcept, Это было бы (используется с осторожностью) даже полезно для уменьшения повторения, как@catch_exception(logAndRethrow).

Похожие вопросы