Python的文档说:
如果不存在表达式,
raise
将重新引发当前作用域中活动的最后一个异常。
(Python 3:https://docs.python.org/3/reference/simple_stmts.html#raise; Python 2.7:https://docs.python.org/2.7/reference/simple_stmts.html#raise。)
但是,“上次激活”的概念似乎已经改变。见证以下代码示例:
#
from __future__ import print_function
import sys
print('Python version =', sys.version)
try:
raise Exception('EXPECTED')
except:
try:
raise Exception('UNEXPECTED')
except:
pass
raise # re-raises UNEXPECTED for Python 2, and re-raises EXPECTED for Python 3
这会导致我在Python 2中出乎意料的东西
Python version = 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)]
Traceback (most recent call last):
File "./x", line 10, in <module>
raise Exception('UNEXPECTED')
Exception: UNEXPECTED
但是使用Python 3可以达到预期的结果(对我来说)
Python version = 3.6.8 (default, Feb 14 2019, 22:09:48)
[GCC 7.4.0]
Traceback (most recent call last):
File "./x", line 7, in <module>
raise Exception('EXPECTED')
Exception: EXPECTED
和
Python version = 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)]
Traceback (most recent call last):
File "./x", line 7, in <module>
raise Exception('EXPECTED')
Exception: EXPECTED
那么“最后一个...活动”是什么意思?是否有一些有关此重大更改的文档?还是这是Python 2错误?
更重要的是:在Python 2中实现此功能的最佳方法是什么? (最好使代码在Python 3中保持正常工作。)
请注意,如果将代码更改为
#
from __future__ import print_function
import sys
print('Python version =', sys.version)
def f():
try:
raise Exception('UNEXPECTED')
except:
pass
try:
raise Exception('EXPECTED')
except:
f()
raise # always raises EXPECTED
然后,事情也开始适用于Python 2:
Python version = 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)]
Traceback (most recent call last):
File "./x", line 13, in <module>
raise Exception('EXPECTED')
Exception: EXPECTED
我正在考虑切换到该位置...
答案 0 :(得分:4)
Python 2的行为与其说是 bug ,还不如说是 design缺陷。在Python 3.0中,通过添加异常链接功能解决了该问题。在PEP 3134 -- Exception Chaining and Embedded Tracebacks 的动机中可以找到与该更改最接近的文档:
在处理一个异常(异常A)期间,可能会发生另一异常(异常B)。在当今的Python(2.4版)中,如果发生这种情况,则异常B向外传播,而异常A丢失。
这正是您在2.7中看到的内容:预期(A)丢失是因为出现了意外(B)并覆盖了它。借助Python 3中更新的异常链接功能,可以通过异常实例上的__cause__
和__context__
属性保留两个错误的完整上下文。
对于更直接的交叉兼容解决方法,我建议您手动保留引用,明确显示正在重新引发哪个错误,并且通常避免使用裸露的except
语句(该语句总是太宽泛) :
try:
raise Exception('EXPECTED')
except Exception as err_expected:
try:
raise Exception('UNEXPECTED')
except Exception as err_unexpected:
pass
raise err_expected
如果您希望以交叉兼容的方式抑制异常链接功能,则可以在重新引发之前通过设置err_expected.__cause__ = None
来实现。
答案 1 :(得分:0)