Python异常链接

Python exception chaining

本问题已经有最佳答案,请猛点这里访问。

在Python中是否有使用异常链的标准方法?像Java异常所引起的?

这是一些背景。

我有一个具有一个主要异常类DSError的模块:

1
2
 class DSError(Exception):
     pass

在这个模块中的某个地方将有:

1
2
3
4
5
6
7
8
9
try:
    v = my_dict[k]
    something(v)
except KeyError as e:
    raise DSError("no key %s found for %s" % (k, self))
except ValueError as e:
    raise DSError("Bad Value %s found for %s" % (v, self))
except DSError as e:
    raise DSError("%s raised in %s" % (e, self))

基本上,这个片段应该只抛出dserror并告诉我发生了什么以及为什么。问题是,try块可能会抛出许多其他异常,因此如果我可以执行以下操作,我更愿意这样做:

1
2
3
4
5
try:
    v = my_dict[k]
    something(v)
except Exception as e:
    raise DSError(self, v, e)  # Exception chained...

这是标准的Python疗法吗?我在其他模块中没有看到异常链,那么在Python中是如何做到的呢?


异常链接仅在Python3中可用,您可以在其中写入:

1
2
3
4
try:
    v = {}['a']
except KeyError as e:
    raise ValueError('failed') from e

产生一个输出

1
2
3
4
5
6
7
8
9
10
11
Traceback (most recent call last):
  File"t.py", line 2, in <module>
    v = {}['a']
KeyError: 'a'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File"t.py", line 4, in <module>
    raise ValueError('failed') from e
ValueError: failed

在大多数情况下,您甚至不需要from;默认情况下,python 3将显示异常处理期间发生的所有异常,如下所示:

1
2
3
4
5
6
7
8
9
10
11
Traceback (most recent call last):
  File"t.py", line 2, in <module>
    v = {}['a']
KeyError: 'a'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File"t.py", line 4, in <module>
    raise ValueError('failed')
ValueError: failed

在python 2中可以做的是向异常类添加自定义属性,比如:

1
2
3
4
5
6
7
8
9
class MyError(Exception):
    def __init__(self, message, cause):
        super(MyError, self).__init__(message + u', caused by ' + repr(cause))
        self.cause = cause

try:
    v = {}['a']
except KeyError as e:
    raise MyError('failed', e)


这就是你想要的吗?

1
2
3
4
5
6
7
8
9
10
11
12
class MyError(Exception):
    def __init__(self, other):
        super(MyError, self).__init__(other.message)

>>> try:
...     1/0
... except Exception, e:
...     raise MyError(e)
Traceback (most recent call last):
  File"<pyshell#27>", line 4, in <module>
    raise MyError(e)
MyError: division by zero

如果要存储原始的异常对象,当然可以在自己的异常类的__init__中进行存储。实际上,您可能希望存储回溯,因为异常对象本身不提供有关异常发生位置的很多有用信息:

1
2
3
4
class MyError(Exception):
    def __init__(self, other):
        self.traceback = sys.exc_info()
        super(MyError, self).__init__(other.message)

之后,您可以访问异常的traceback属性,以获取有关原始异常的信息。(python 3已经将其作为异常对象的__traceback__属性提供。)