簡體   English   中英

Python "raise from" 用法

[英]Python "raise from" usage

raiseraise 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隱式設置 exc exc.__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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM