dar*_*ine 154 python syntax exception-handling python-3.x
Python raise和raise fromPython 之间的区别是什么?
try:
raise ValueError
except Exception as e:
raise IndexError
Run Code Online (Sandbox Code Playgroud)
产量
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
Run Code Online (Sandbox Code Playgroud)
和
try:
raise ValueError
except Exception as e:
raise IndexError from e
Run Code Online (Sandbox Code Playgroud)
产量
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
Run Code Online (Sandbox Code Playgroud)
Mar*_*ers 180
不同之处在于,在使用时from,会设置__cause__属性并且消息指出异常是由直接引起的.如果省略则设置fromno __cause__,但也可以设置__context__属性,然后traceback将在处理其他事件时显示上下文.
__context__如果您raise在异常处理程序中使用,则设置发生; 如果你raise在其他地方使用过,也没有__context__设置.
如果__cause__设置了a,__suppress_context__ = True则还在异常上设置标志; 如果__suppress_context__设置为True,__context__则在打印回溯时将忽略该值.
从异常处理程序引发时,您不希望显示上下文(不希望处理期间发生另一个异常消息),然后使用raise ... from None设置__suppress_context__为True.
换句话说,Python 在异常上设置了一个上下文,这样你就可以反省引发异常的位置,让你看看是否有另一个异常被它取代.您还可以向异常添加原因,使回溯显式关于另一个异常(使用不同的措辞),并忽略上下文(但在调试时仍然可以进行内省).使用raise ... from None可以抑制正在打印的上下文.
请参阅raise声明文件:
该
from子句用于异常链接:如果给定,则第二个表达式必须是另一个异常类或实例,然后将其作为__cause__属性(可写)附加到引发的异常.如果未处理引发的异常,则将打印两个异常:Run Code Online (Sandbox Code Playgroud)>>> 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属性:Run Code Online (Sandbox Code Playgroud)>>> 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
另请参阅内置异常文档,以获取有关上下文的详细信息以及附加到异常的原因信息.
Mag*_*ero 19
2005 年,PEP 3134、异常链和嵌入式回溯引入了异常链:
\nraise EXCEPTION或隐式 raise (__context__属性)的隐式链接;raise EXCEPTION from CAUSE(__cause__属性)的显式链接。\n\n动机
\n在处理一个异常(异常A)的过程中,有可能发生另一异常(异常B)。在今天\xe2\x80\x99s的Python(版本2.4)中,如果发生这种情况,异常B会向外传播,异常A会丢失。为了调试问题,了解这两个异常很有用。该
\n__context__属性自动保留此信息。有时,异常处理程序有意重新引发异常可能很有用,可以提供额外的信息,也可以将异常转换为另一种类型。该
\n__cause__属性提供了一种显式的方式来记录异常的直接原因。[\xe2\x80\xa6]
\n隐式异常链接
\n下面是一个例子来说明该
\n__context__属性:Run Code Online (Sandbox Code Playgroud)\ndef compute(a, b):\n try:\n a/b\n except Exception, exc:\n log(exc)\n\ndef log(exc):\n file = open(\'logfile.txt\') # oops, forgot the \'w\'\n print >>file, exc\n file.close()\n调用
\ncompute(0, 0)会导致ZeroDivisionError. 该compute()函数捕获此异常并调用log(exc),但当该log()函数尝试写入未打开用于写入的\xe2\x80\x99t 文件时,也会引发异常。在今天的\xe2\x80\x99s Python 中,调用者
\ncompute()会抛出一个IOError. 丢失ZeroDivisionError了。通过建议的更改, 的实例IOError具有__context__保留ZeroDivisionError.[\xe2\x80\xa6]
\n显式异常链接
\n异常对象的属性
\n__cause__始终初始化为None。它由新的语句形式设置raise:Run Code Online (Sandbox Code Playgroud)\nraise EXCEPTION from CAUSE\n这相当于:
\nRun Code Online (Sandbox Code Playgroud)\nexc = EXCEPTION\nexc.__cause__ = CAUSE\nraise exc\n在下面的示例中,数据库提供了几种不同类型存储的实现,其中文件存储是一种。数据库设计者希望错误作为
\nDatabaseError对象传播,以便客户端不必了解特定于存储的详细信息,但又不想丢失底层错误信息。Run Code Online (Sandbox Code Playgroud)\nclass DatabaseError(Exception):\n pass\n\nclass FileDatabase(Database):\n def __init__(self, filename):\n try:\n self.file = open(filename)\n except IOError, exc:\n raise DatabaseError(\'failed to open\') from exc\n如果对 的调用
\nopen()引发异常,则问题将报告为 aDatabaseError,并带有一个将 揭示为原始原因的__cause__属性。IOError增强报告功能
\n默认异常处理程序将被修改以报告链式异常。
\n__cause__通过遵循和属性来遍历异常链__context__,并__cause__优先考虑。为了与回溯的时间顺序保持一致,最近引发的异常显示在最后;也就是说,显示从最内层异常的描述开始,并将链备份到最外层异常。回溯的格式与往常一样,包含以下行之一:\n\n上述异常是导致以下异常的直接原因:
\n或者
\n\n\n在处理上述异常的过程中,又出现了一个异常:
\n\n
__cause__回溯之间,取决于它们是否由或分别链接__context__。以下是该过程的草图:Run Code Online (Sandbox Code Playgroud)\ndef print_chain(exc):\n if exc.__cause__:\n print_chain(exc.__cause__)\n print \'\\nThe above exception was the direct cause...\'\n elif exc.__context__:\n print_chain(exc.__context__)\n print \'\\nDuring handling of the above exception, ...\'\n print_exc(exc)\n[\xe2\x80\xa6]
\n
2012 年,PEP 415“使用异常属性实现上下文抑制”raise EXCEPTION from None引入了使用显式(属性)的异常上下文抑制__suppress_context__。
\n\n提议
\n将引入
\nBaseException,上的新属性。__suppress_context__每当__cause__设置时,__suppress_context__都会设置为True。特别是,raise exc from cause语法将设置exc.__suppress_context__为True. 异常打印代码将检查该属性以确定是否打印上下文和原因。__cause__将回归其最初的目的和价值。\n
__suppress_context__异常属性具有优先权print_line_and_file。总而言之,
\nraise exc from cause将相当于:Run Code Online (Sandbox Code Playgroud)\nexc.__cause__ = cause\nraise exc\n其中
\nexc.__cause__ = cause隐式设置exc.__suppress_context__.
因此,在 PEP 415 中,PEP 3134 中给出的默认异常处理程序(其工作是报告异常)的过程概述如下:
\ndef print_chain(exc):\n if exc.__cause__:\n print_chain(exc.__cause__)\n print \'\\nThe above exception was the direct cause...\'\n elif exc.__context__ and not exc.__suppress_context__:\n print_chain(exc.__context__)\n print \'\\nDuring handling of the above exception, ...\'\n print_exc(exc)\nRun Code Online (Sandbox Code Playgroud)\n
最短的答案。PEP-3134说明了一切。raise Exception from e设置__cause__新异常的字段。
来自同一个 PEP 的更长答案:
__context__字段将隐式设置为except:块内的原始错误,除非被告知不要使用__suppress_context__ = True。__cause__就像上下文一样,但必须使用from语法显式设置tracebackraise当您在块内调用时将始终链接except。except: pass您可以通过a)吞掉异常或直接搞乱来摆脱回溯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)
Run Code Online (Sandbox Code Playgroud)
将产生以下输出:
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
Run Code Online (Sandbox Code Playgroud)