【问题标题】:Python exception chaining [duplicate]Python异常链接[重复]
【发布时间】:2013-05-01 03:40:02
【问题描述】:

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

这里有一些背景。

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

 class DSError(Exception):
     pass

在这个模块的某个地方会有:

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))

基本上这个 sn-p 应该只抛出 DSError 并告诉我发生了什么以及为什么。问题是 try 块可能会抛出许多其他异常,所以如果我可以执行以下操作,我会更喜欢:

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

这是标准的pythonic方式吗?我没有在其他模块中看到异常链,那么在 Python 中是如何完成的?

【问题讨论】:

  • 您希望输出是什么?我不知道您是否真的想要原始异常的堆栈跟踪,或者您是否想隐藏它并且只使用一条总结原始异常的消息来拥有自己的异常?
  • 原始跟踪会好很多,因为可以从模块递归调用 try 块。

标签: python exception coding-style python-2.x


【解决方案1】:

Exception chaining 仅在 Python 3 中可用,您可以在其中编写:

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

产生类似的输出

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 将显示在异常处理期间发生的所有异常,如下所示:

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 中为您的异常类添加自定义属性,例如:

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)

【讨论】:

  • 对于 python 2,如果想要保存回溯 - 必须想要 - raise MyError(message + u', caused by ' + repr(cause)), None, sys.exc_info()[2]
  • “在大多数情况下,你甚至不需要 from”你有一个需要或有用的例子吗?
  • @timgeb PEP 3134 有两种链接情况:一种是错误处理代码导致引发另一个异常,另一种是故意将异常转换为不同的异常。 from e 用于故意情况,并更改输出中的消息,如上面的答案所示。
  • 在我看来,此响应比重复问题的选定答案更好,因为这涵盖了 Python 3。此外,还感谢您注意到 from 甚至没有必要,因为 Python 3 回溯已经打印了堆栈中的所有当前异常。
  • 点赞!如果你也想从这里返回一个值,你会怎么做
【解决方案2】:

这是你要求的吗?

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__ 中这样做。您实际上可能希望存储回溯,因为异常对象本身并没有提供太多关于异常发生位置的有用信息:

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

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

【讨论】:

  • 几乎正确,但我认为这将是“作弊”,因为它只接收链接异常的消息,而不是实际的异常对象。 IE。我不知道实际除以零发生在哪里,只是在某处被捕获。
  • @Ayman:查看我编辑的答案。您所要做的就是获取回溯并存储它。但是,如果您真的希望原始异常中的所有信息都像真正的异常一样显示在回溯中,那么 phihag 是正确的,这在 Python 2 中无法实现。您必须手动打印旧的回溯作为异常消息的一部分。
  • 谢谢。我不知道 sys.exc_info()。我也会接受这个作为答案:-)
  • other.message 是否始终存在?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-21
  • 2014-10-23
  • 2014-01-02
  • 2014-09-18
  • 1970-01-01
相关资源
最近更新 更多