[英]Python "raise from" usage
raise
和raise from
in Python 有什么区别?
try:
raise ValueError
except Exception as e:
raise IndexError
哪个产量
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError
IndexError
和
try:
raise ValueError
except Exception as e:
raise IndexError from e
哪个产量
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError from e
IndexError
不同之处在于,当您使用from
,会设置__cause__
属性,并且消息指出异常是由 直接引起的。 如果省略from
则不会设置__cause__
,但也可以设置__context__
属性,然后回溯显示上下文,如处理其他发生的事情。
如果您在异常处理程序中使用raise
,则会设置__context__
; 如果您在其他任何地方使用raise
不会设置__context__
。
如果__cause__
被设置,一个__suppress_context__ = True
标志也被设置例外上; 当__suppress_context__
设置为True
时,打印回溯时会忽略__context__
。
从异常处理程序,你不希望显示的背景下提出的时候,再使用(不处理另一个异常发生的消息时希望) raise ... from None
到组__suppress_context__
至True
。
换句话说,Python 为异常设置了一个上下文,以便您可以自省引发异常的位置,让您查看是否有另一个异常被它替换。 您还可以为异常添加原因,使回溯明确关于另一个异常(使用不同的措辞),并且上下文被忽略(但在调试时仍然可以自省)。 使用raise ... from None
可以抑制正在打印的上下文。
请参阅raise
语句文档:
from
子句用于异常链接:如果给定,第二个表达式必须是另一个异常类或实例,然后将作为__cause__
属性(可写)附加到引发的异常。 如果未处理引发的异常,则将打印两个异常:>>> try: ... print(1 / 0) ... except Exception as exc: ... raise RuntimeError("Something bad happened") from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened
如果在异常处理程序或
finally
子句中引发异常,则类似的机制会隐式工作:然后将前一个异常附加为新异常的__context__
属性:>>> try: ... print(1 / 0) ... except: ... raise RuntimeError("Something bad happened") ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened
另请参阅内置异常文档以获取有关上下文和附加到异常的原因信息的详细信息。
PEP 3134,Exception Chaining 和 Embedded Tracebacks引入了异常链接(通过显式raise EXCEPTION
或隐式引发进行隐式链接,并raise EXCEPTION from CAUSE
进行显式链接)。 以下是了解其用法的相关段落:
动机
在处理一个异常(异常A)的过程中,可能会发生另一个异常(异常B)。 在今天的Python(2.4版本)中,如果出现这种情况,异常B向外传播,异常A丢失。 为了调试问题,了解这两个异常是很有用的。
__context__
属性自动保留此信息。有时,异常处理程序有意重新引发异常以提供额外信息或将异常转换为另一种类型可能很有用。
__cause__
属性提供了一种明确的方式来记录异常的直接原因。[…]
隐式异常链接
下面是一个示例来说明
__context__
属性:def compute(a, b): try: a/b except Exception, exc: log(exc) def log(exc): file = open('logfile.txt') # oops, forgot the 'w' print >>file, exc file.close()
调用
compute(0, 0)
会导致ZeroDivisionError
。compute()
function 捕获此异常并调用log(exc)
,但log()
function 在尝试写入未打开进行写入的文件时也会引发异常。在今天的 Python 中,
compute()
的调用者被抛出一个IOError
。ZeroDivisionError
丢失了。 根据提议的更改,IOError
的实例有一个额外的__context__
属性,该属性保留ZeroDivisionError
。[…]
显式异常链接
异常对象的
__cause__
属性总是初始化为None
。 它由一种新形式的raise
语句设置:raise EXCEPTION from CAUSE
这相当于:
exc = EXCEPTION exc.__cause__ = CAUSE raise exc
在下面的示例中,数据库提供了几种不同存储类型的实现,文件存储就是其中一种。 数据库设计者希望错误作为
DatabaseError
对象传播,这样客户端就不必知道特定于存储的详细信息,但又不想丢失底层错误信息。class DatabaseError(Exception): pass class FileDatabase(Database): def __init__(self, filename): try: self.file = open(filename) except IOError, exc: raise DatabaseError('failed to open') from exc
如果对
open()
的调用引发异常,该问题将被报告为DatabaseError
,并带有一个__cause__
属性,该属性将IOError
显示为原始原因。增强报告
默认的异常处理程序将被修改为报告链式异常。 异常链通过遵循
__cause__
和__context__
属性来遍历,其中__cause__
优先。 为了与回溯的时间顺序保持一致,最近引发的异常显示在最后; 也就是说,显示从最内层异常的描述开始,然后将链备份到最外层异常。 回溯像往常一样格式化,其中一行:上述异常是以下异常的直接原因:
要么
在处理上述异常的过程中,又出现了一个异常:
在回溯之间,取决于它们是否分别由
__cause__
或__context__
链接。 这是程序的草图:def print_chain(exc): if exc.__cause__: print_chain(exc.__cause__) print '\nThe above exception was the direct cause...' elif exc.__context__: print_chain(exc.__context__) print '\nDuring handling of the above exception, ...' print_exc(exc)
[…]
PEP 415,使用异常属性实现上下文抑制然后引入了异常上下文的抑制( raise EXCEPTION from None
)。 这是了解其用法的相关段落:
提议
__suppress_context__
上的一个新属性BaseException
将被引入。 每当设置__cause__
时,__suppress_context__
将设置为True
。 特别是,raise exc from cause
语法会将exc.__suppress_context__
设置为True
。 异常打印代码将检查该属性以确定是否打印上下文和原因。__cause__
将恢复到其最初的目的和价值。
__suppress_context__
优先于print_line_and_file
异常属性。总而言之,
raise exc from cause
等同于:exc.__cause__ = cause raise exc
其中
exc.__cause__ = cause
隐式设置 excexc.__suppress_context__
。
所以在 PEP 415 中,PEP 3134 中给出的过程的草图变成如下:
def print_chain(exc):
if exc.__cause__:
print_chain(exc.__cause__)
print '\nThe above exception was the direct cause...'
elif exc.__context__ and not exc.__suppress_context__:
print_chain(exc.__context__)
print '\nDuring handling of the above exception, ...'
print_exc(exc)
最短的答案。 PEP-3134说明了一切。 raise Exception from e
设置新异常的__cause__
。
来自同一 PEP的更长答案:
__context__
字段将隐式设置为内部的原始错误except:
block 除非被告知不要使用__suppress_context__ = True
。__cause__
就像上下文一样,但必须使用from
语法显式设置except
块内调用raise
时, traceback
将始终链接。 您可以通过 a) 吞下异常except: pass
或直接弄乱sys.exc_info()
来摆脱回溯。长答案
import traceback
import sys
class CustomError(Exception):
def __init__(self):
super().__init__("custom")
def print_exception(func):
print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
try:
func()
except Exception as e:
"Here is result of our actions:"
print(f"\tException type: '{type(e)}'")
print(f"\tException message: '{e}'")
print(f"\tException context: '{e.__context__}'")
print(f"\tContext type: '{type(e.__context__)}'")
print(f"\tException cause: '{e.__cause__}'")
print(f"\tCause type: '{type(e.__cause__)}'")
print("\nTRACEBACKSTART>>>")
traceback.print_exc()
print("<<<TRACEBACKEND")
def original_error_emitter():
x = {}
print(x.does_not_exist)
def vanilla_catch_swallow():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
pass
def vanilla_catch_reraise():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise e
def catch_replace():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError()
def catch_replace_with_from():
"""Nothing is expected to happen"""
try:
original_error_emitter()
except Exception as e:
raise CustomError() from e
def catch_reset_trace():
saw_an_error = False
try:
original_error_emitter()
except Exception as e:
saw_an_error = True
if saw_an_error:
raise CustomError()
print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)
将导致以下 output:
Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow'
Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise'
Exception type: '<class 'AttributeError'>'
Exception message: ''dict' object has no attribute 'does_not_exist''
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
raise e
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND
Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND
Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: ''dict' object has no attribute 'does_not_exist''
Context type: '<class 'AttributeError'>'
Exception cause: ''dict' object has no attribute 'does_not_exist''
Cause type: '<class 'AttributeError'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
original_error_emitter()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND
Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace'
Exception type: '<class '__main__.CustomError'>'
Exception message: 'custom'
Exception context: 'None'
Context type: '<class 'NoneType'>'
Exception cause: 'None'
Cause type: '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
func()
File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
raise CustomError()
CustomError: custom
<<<TRACEBACKEND
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.