1. 程式人生 > >Python 中 raise 和 raise/from 的區別

Python 中 raise 和 raise/from 的區別

Python 中 raise 和 raise/from 的使用方法


文章目錄


0. 參考資料


1. 程式碼比較

今天在看《Effective Python》的時候第一次見到 raise A from B 的用法,所以在網上查了一下。
下面用程式碼比較一下 raiseraise/from 的區別。

  • raise.py
# raise
try:
    raise ValueError
except Exception as e:
    raise IndexError
"""
Traceback (most recent call last):
  File "raise.py", line 3, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "raise.py", line 5, in <module>
    raise IndexError
IndexError
"""
  • raisefrom.py
# raise/from
try:
    raise ValueError
except Exception as e:
    raise IndexError from e
"""
Traceback (most recent call last):
  File "raisefrom.py", line 3, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "raisefrom.py", line 5, in <module>
    raise IndexError from e
IndexError
"""

上面的 raiseraise/from 都是放在 except 異常處理塊中的。
可以觀察到 raiseraise/from 最大的區別是異常提示資訊不一樣:

  • raise 是:During handling of the above exception, another exception occurred:
    即“在處理上面的異常時,發生了另外一個異常:”。
  • raise/from 是:The above exception was the direct cause of the following exception:
    即“上面的異常是接下來的異常的直接原因:”。

2. 用法解釋

2.1 raise

當在 except 塊或者 finally 塊中出現異常時(包括使用單獨的 raise 重新丟擲異常的情況),之前的異常會被附加到新異常的 __context__ 屬性上

except 塊中的語句叫做異常處理器 exception handler,它們是處理異常的語句。

而在其他任何地方丟擲異常,都不會設定 __context__ 屬性。
這樣打印出來的異常資訊就會包含這麼一句話:During handling of the above exception, another exception occurred:


2.2 raise A from B

raise A from B 語句用於連鎖 chain 異常。
from 後面的 B 可以是:

  • 異常類
  • 異常例項
  • NonePython 3.3 的新特性)

如果 B 是異常類或者異常例項,那麼 B 會被設定為 A__cause__ 屬性,表明 A異常 是由 B異常 導致的。
這樣打印出來的異常資訊就會包含這樣一句話:The above exception was the direct cause of the following exception:
與此同時,在 Python 3.3A異常__suppress_context__ 屬性會被設定為 True,這樣就抑制A異常__context__ 屬性,即忽略 __context__ 屬性。
於是 Python不會自動列印異常上下文 exception context,而是使用 __cause__ 屬性來列印異常的引發者。

Python 3.3 中,B 還可以是 Noneraise A異常 from None
這樣相當於把 __suppress_context__ 屬性設定為 True,從而禁用了 __context__ 屬性,Python 不會自動展示異常上下文。
比如下面這段程式碼,注意到只顯示了 IndexError 一個異常資訊:
raisefromnone.py

# raise ... from None
# 禁用異常上下文屬性
try:
    raise ValueError
except Exception as e:
    raise IndexError from None
"""
Traceback (most recent call last):
  File "raisefromnone.py", line 6, in <module>
    raise IndexError from None
IndexError
"""


3. 總結

except 或者 finally 塊中使用 raiseraise/from 語句需要注意:

  • raise 會設定後面異常的 __context__ 屬性為前面的異常。
    異常資訊中會有 During handling of the above exception, another exception occurred:
  • raise A異常 from B異常 會把 A異常__cause__ 屬性設定為 B異常
    同時設定 __suppress_context__ 屬性為 True,從而忽略 __context__ 屬性,不列印異常上下文資訊。
    異常資訊中會有 The above exception was the direct cause of the following exception:
  • raise A異常 from None 會設定 A異常__suppress_context__ 屬性為 True,這樣會忽略它的 __context__ 屬性,不會自動顯示異常上下文資訊。

完成於 2018.11.21