Python 'raise' 没有参数:什么是 "the last exception that was active in the current scope"?
Python 'raise' without arguments: what is "the last exception that was active in the current scope"?
Python 的文档说:
If no expressions are present, raise
re-raises the last exception that was active in the current scope.
(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.)
不过,"last active"的观念似乎变了。见证以下代码示例:
#
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
那么"the last ... active"是什么意思呢?是否有关于此重大更改的一些文档?或者这是一个 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
我正在考虑改用那个...
raise
使用与 sys.exc_info
相同的信息,记录 both behaviors。由于记录了您的变通方法利用的 per-frame 行为,这就是要走的路。
PEP 3110 对 except
语句进行了一些更改。我相信它包括这个,但唯一明确提到的是 as
存储的异常在离开 except
.
时被丢弃
Python 2 的行为与其说是 错误 ,不如说是 设计缺陷 。 Python 3.0 通过添加异常链接功能解决了这个问题。可以在 PEP 3134 -- Exception Chaining and Embedded Tracebacks 中找到最接近此更改的文档
动机:
During the handling of one exception (exception A), it is possible that another exception (exception B) may occur. In today's Python (version 2.4), if this happens, exception B is propagated outward and exception A is lost.
这正是您在 2.7 中看到的:EXPECTED (A) 丢失,因为 UNEXPECTED (B) 出现并覆盖了它。使用 Python 3 中较新的异常链接功能,可以通过异常实例上的 __cause__
和 __context__
属性保留两个错误的完整上下文。
对于更直接的 cross-compatible 解决方法,我鼓励您手动保留引用,明确显示 re-raised 是哪个错误,并且像往常一样避免裸露的 except
语句(总是太宽泛):
try:
raise Exception('EXPECTED')
except Exception as err_expected:
try:
raise Exception('UNEXPECTED')
except Exception as err_unexpected:
pass
raise err_expected
如果您希望以 cross-compatible 的方式抑制 exception-chaining 功能,您可以通过在 re-raising.
之前设置 err_expected.__cause__ = None
来实现
Python 的文档说:
If no expressions are present,
raise
re-raises the last exception that was active in the current scope.
(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.)
不过,"last active"的观念似乎变了。见证以下代码示例:
#
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
那么"the last ... active"是什么意思呢?是否有关于此重大更改的一些文档?或者这是一个 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
我正在考虑改用那个...
raise
使用与 sys.exc_info
相同的信息,记录 both behaviors。由于记录了您的变通方法利用的 per-frame 行为,这就是要走的路。
PEP 3110 对 except
语句进行了一些更改。我相信它包括这个,但唯一明确提到的是 as
存储的异常在离开 except
.
Python 2 的行为与其说是 错误 ,不如说是 设计缺陷 。 Python 3.0 通过添加异常链接功能解决了这个问题。可以在 PEP 3134 -- Exception Chaining and Embedded Tracebacks 中找到最接近此更改的文档 动机:
During the handling of one exception (exception A), it is possible that another exception (exception B) may occur. In today's Python (version 2.4), if this happens, exception B is propagated outward and exception A is lost.
这正是您在 2.7 中看到的:EXPECTED (A) 丢失,因为 UNEXPECTED (B) 出现并覆盖了它。使用 Python 3 中较新的异常链接功能,可以通过异常实例上的 __cause__
和 __context__
属性保留两个错误的完整上下文。
对于更直接的 cross-compatible 解决方法,我鼓励您手动保留引用,明确显示 re-raised 是哪个错误,并且像往常一样避免裸露的 except
语句(总是太宽泛):
try:
raise Exception('EXPECTED')
except Exception as err_expected:
try:
raise Exception('UNEXPECTED')
except Exception as err_unexpected:
pass
raise err_expected
如果您希望以 cross-compatible 的方式抑制 exception-chaining 功能,您可以通过在 re-raising.
之前设置err_expected.__cause__ = None
来实现