[英]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.