如何在装饰器中捕获异常并让调用者也能捕获?

16 投票
2 回答
13141 浏览
提问于 2025-04-16 07:28

我有一个Python函数,这个函数可能会出现错误(我们称之为异常)。调用这个函数的人会捕捉到这个错误并进行处理。现在我想给这个函数加一个装饰器,这个装饰器也能捕捉到这个错误,进行一些处理,但之后还要把这个错误重新抛出,让最初调用的人来处理。这样做是可以的,但有一个问题:当最初调用的人查看错误的调用栈时,显示的是装饰器中重新抛出错误的那一行,而不是错误最初发生的地方。下面是一个示例代码:

import sys,traceback

def mydec(func):
    def dec():
        try:
            func()
        except Exception,e:
            print 'Decorator handled exception %s' % e
            raise e
    return dec

@mydec
def myfunc():
    x = 1/0

try:
    myfunc()
except Exception,e:
    print 'Exception: %s' % e
    type,value,tb = sys.exc_info()
    traceback.print_tb(tb)

输出结果是:

Decorator handled exception integer division or modulo by zero
Exception: integer division or modulo by zero
  File "allbug.py", line 20, in <module>
    myfunc()
  File "allbug.py", line 9, in dec
    raise e

我希望这个装饰器能够处理这个错误,但错误的追踪信息应该指向x = 1/0这一行,而不是raise这一行。我该怎么做呢?

2 个回答

8

我刚写了一个类,跟你做的差不多,不过提供了更多的选项。下面是我的代码:

class ErrorIgnore(object):
   def __init__(self, errors, errorreturn = None, errorcall = None):
      self.errors = errors
      self.errorreturn = errorreturn
      self.errorcall = errorcall

   def __call__(self, function):
      def returnfunction(*args, **kwargs):
         try:
            return function(*args, **kwargs)
         except Exception as E:
            if type(E) not in self.errors:
               raise E
            if self.errorcall is not None:
               self.errorcall(E, *args, **kwargs)
            return self.errorreturn
      return returnfunction

常见的用法可能是这样的:

def errorcall(E, *args):
   print 'exception skipped', E

@ErrorIgnore(errors = [ZeroDivisionError, ValueError], errorreturn = None, errorcall = errorcall)
def myfunction(stuff):
   # do stuff
   # return stuff
   # the errors shown are skipped
19

在一个 catch 块里,只需要使用 raise;(也就是说,不需要指定任何具体的错误,只用 raise;)就可以重新抛出异常,而不会“重置”错误的追踪信息。

撰写回答