【问题标题】:Catch exceptions inside a class在类中捕获异常
【发布时间】:2012-07-10 08:26:14
【问题描述】:

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

更新:感谢所有有见地的答案,装饰器的想法看起来是要走的路。 关于捕获所有异常的风险:except 分支中的 raise 语句应该重新引发异常而不会丢失任何信息,不是吗?这就是为什么我也把它放在 MyError 中......

【问题讨论】:

标签: python exception-handling


【解决方案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 等...)

【讨论】:

  • +1 表示“你很可能 [真的] 不想要这样的东西”。
  • 为了使它以某种方式更优雅,我将添加一个handler 参数,该参数在except 中使用。这将(谨慎使用)甚至有助于减少重复,例如@catch_exception(logAndRethrow)
  • @phg 我明白你的意思了——希望你不介意,但我会把它留给 OP。
  • +1 对于演示单独很有用,但我想再次向 OP 强调,这确实是个坏主意...
  • 你能解释一下“你真的不想要这样的东西”吗?我有许多包含许多类的脚本,每个类都有许多方法。我想知道哪个类/方法失败了,而不需要为每个类/方法编写一个尝试/除了
【解决方案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

元类在定义Test 时将catch_exception 应用于看似方法的所有内容。


为了响应关于每个方法的自定义消息的评论,可以将这样的消息(甚至是用于生成消息的回调函数)附加为属性:

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__ 的好例子 - 如果 OP 最终要在自己的脚下开枪,至少在我们之间,我们提供了示例以确保他们立即将其击毁。 ... 咳嗽
  • 我在这里遇到的最好的答案是 SO。有没有办法为每个方法自定义异常消息?
【解决方案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] 是当前函数的函数名。您可以使用这些来创建异常属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多