18

是否可以编写一个异常处理程序来捕获类中所有方法生成的运行时错误?我可以通过用 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 中......

4

3 回答 3

25

警告:如果你想要这样的东西,很可能你不会……但如果你真的想要……

就像是:

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'sproperties

于 2012-07-10T19:40:18.260 回答
18

假设你有一个装饰器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
于 2012-07-10T19:55:19.883 回答
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] 是当前函数的函数名。您可以使用这些来创建异常属性。

于 2012-07-10T19:53:14.907 回答