摘要:下面我们来看一下效果下次需要捕获一个异常然后再抛出另一个异常的时候大家可以试试本文的方法。
一般实现捕获异常然后再抛出另一个异常的方法类似下面这样:
def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e)
不知道大家有没有注意到这样抛出异常的方式有一个很严重的问题,那就是 在重新抛出另一个异常的时候,捕获的上一个异常的 traceback 信息丢失了(python2): :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) $ python2 a.py Traceback (most recent call last): File "a.py", line 6, inraise ValueError(e) ValueError: integer division or modulo by zero
这样的话非常不利于查找问题: 比如上面的例子中实际出错的代码是第二行,但是 当我们捕获了第一个异常然后再抛出一个自定义异常的时候, 实际出错位置的信息就丢失了。
Python 2那么在 Python 2 下如果我们不想丢失捕获的异常的 traceback 信息的话,应该 怎样重新抛出异常呢?
有两种办法, 还是用上面的例子举例:
一种办法是直接 raise: :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise $ python2 a.py Traceback (most recent call last): File "a.py", line 4, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: integer division or modulo by zero
另一种办法就是 raise 另一个异常时指定上一个异常的 traceback 信息 (通过 sys.exc_info() 获取当前捕获的异常信息): :
$ cat a.py import sys def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e), None, sys.exc_info()[2] $ python2 a.py Traceback (most recent call last): File "a.py", line 6, indiv() File "a.py", line 4, in div 2 / 0 ValueError: integer division or modulo by zero
这个是 raise 的高级用法:
raise exception, value, traceback
exception: 异常类实例/异常类
value: 初始化异常类的参数值/异常类实例(使用这个实例作为 raise 的异常实例)/元组/None
traceback: traceback 对象/None
下面我们来看看上面的方法是否可以应对多层异常捕获然后再抛出的情况: :
$ cat a.py import sys def div(): 2 / 0 def foo(): try: div() except ZeroDivisionError as e: raise ValueError(e), None, sys.exc_info()[2] def bar(): try: foo() except ValueError as e: raise TypeError(e), None, sys.exc_info()[2] def foobar(): try: bar() except TypeError as e: raise foobar() $ python2 a.py Traceback (most recent call last): File "a.py", line 23, infoobar() File "a.py", line 20, in foobar bar() File "a.py", line 14, in bar foo() File "a.py", line 8, in foo div() File "a.py", line 4, in div 2 / 0 TypeError: integer division or modulo by zero
从上面的结果可以看到这两种方法是支持多层异常 traceback 信息传递的。
那么在 Python 3 下又怎么解决这个问题呢?
Python 3在 Python 3 下默认会附加上捕获的上个异常的 trackback 信息(保存在异常实例的 __traceback__ 属性中): :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) $ python3 a.py Traceback (most recent call last): File "a.py", line 4, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 6, in raise ValueError(e) ValueError: division by zero
也支持指定使用哪个异常实例的 traceback 信息: raise ... from ... :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) from e $ python a.py Traceback (most recent call last): File "a.py", line 5, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: division by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "a.py", line 7, in raise ValueError(e) from e ValueError: division by zero
也可以指定使用的 traceback 对象: raise exception.with_traceback(traceback) :
$ cat a.py import sys def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e).with_traceback(sys.exc_info()[2]) $ python a.py Traceback (most recent call last): File "a.py", line 7, in兼容 Python 2 和 Python 3 的写法div() File "a.py", line 4, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 9, in raise ValueError(e).with_traceback(sys.exc_info()[2]) File "a.py", line 7, in div() File "a.py", line 4, in div 2 / 0 ValueError: division by zero
上面介绍了在 Python 2 和 Python 3 下的不同解决办法,那么如何写一个兼容 Python 2 和 Python 3 的 reraise 函数呢?
下面将介绍一种方法:
PY3 = sys.version_info[0] == 3 if PY3: def reraise(tp, value, tb=None): if value.__traceback__ is not tb: raise value.with_traceback(tb) else: raise value else: exec("""def reraise(tp, value, tb=None): raise tp, value, tb """)
这里的 reraise 函数我们约定了 vlaue 参数的值是一个异常类的实例。 上面 else 中之所以用 exec 去定义 reraise 函数是因为 raise tp, value, tb 在 Python 3 下会报语法错误,所以用 exec 来 绕过 Python 3 下的语法错误检查。
下面我们来看一下效果: :
$ cat a.py ef div(): 2 / 0 def foo(): try: div() except ZeroDivisionError as e: reraise(ValueError, ValueError(e), sys.exc_info()[2]) def bar(): try: foo() except ValueError as e: reraise(TypeError, TypeError(e), sys.exc_info()[2]) def foobar(): try: bar() except TypeError: raise foobar()
Python 2: :
$ python2 a.py Traceback (most recent call last): File "a.py", line 34, infoobar() File "a.py", line 31, in foobar bar() File "a.py", line 27, in bar reraise(TypeError, TypeError(e), sys.exc_info()[2]) File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 TypeError: integer division or modulo by zero
Python 3: :
$ python3 a.py Traceback (most recent call last): File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 ValueError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 34, infoobar() File "a.py", line 31, in foobar bar() File "a.py", line 27, in bar reraise(TypeError, TypeError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 TypeError: division by zero
下次需要捕获一个异常然后再抛出另一个异常的时候大家可以试试本文的方法。
参考资料6. Simple statements — Python 2.7.12 documentation
6. Built-in Exceptions — Python 2.7.12 documentation
7. Simple statements — Python 3.5.2 documentation
5. Built-in Exceptions — Python 3.5.2 documentation
PEP 3109 -- Raising Exceptions in Python 3000 | Python.org
bottle/bottle.py at cafc15419cbb4a6cb748e6ecdccf92893bb25ce5 · bottlepy/bottle
flask/_compat.py at 6e46d0cd3969f6c13ff61c95c81a975192232fed · pallets/flask
原文地址: https://mozillazg.com/2016/08...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/38180.html
摘要:我们使用单元测试来验证一下我们使用了配合做单元测试。我们编写相应的单元测试你会发现,如果出现异常,只是简单的返回。但是在上面异常抛出的时候,解释器已经不在中了,因此无法被捕获。 译者按: 错误是无法避免的,妥善处理它才是最重要的! 原文: A Guide to Proper Error Handling in JavaScript Related Topics: 译者: Funde...
摘要:一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。这种在编译时被强制检查的异常称为被检查的异常。通过获取原始异常。构造器对于在构造阶段可能会抛出异常,并要求清理的类,最安全的做法是使用嵌套的子句。 点击进入我的博客 Java异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更自信:你的应用中没有未处理的错误。 12.1 概念 异...
摘要:如何良好的在代码中设计异常机制本身设计的出发点是极好的,通过编译器的强制捕获,可以明确提醒调用者处理异常情况。但使用此种异常后,该会像病毒一样,得不到处理后会污染大量代码,同时也可能因为调用者的不当处理,会失去异常信息。 1、异常是什么? 父类为Throwable,有Error和Exception两个子类 Error为系统级别的异常(错误) Exception下有众多子类,常见的有Ru...
摘要:对异常的处理方法是打印异常的跟踪栈信息并终止程序运行。应尽量对异常进行适当的处理,而不是简单的将异常跟踪栈信息打印出来。 一、异常概述 开发者都希望所有错误都能在编译阶段被发现,就是试图在运行程序之前排除所有错误,但这是不现实的,余下问题必须在运行期间得到解决。 Java将异常分为两种:CheckedException和RuntimeException。其中,CheckedExcept...
阅读 3222·2021-11-15 11:37
阅读 1012·2021-11-02 14:45
阅读 3873·2021-09-04 16:48
阅读 3553·2019-08-30 15:55
阅读 721·2019-08-23 17:53
阅读 963·2019-08-23 17:03
阅读 1997·2019-08-23 16:43
阅读 2166·2019-08-23 16:22