原地址:http://openhome.cc/Gossip/Python/TryRaise.html
except後若不接上任何例外型態,則表示捕捉所有例外,這包括了所有的系統例外,有時這並不是你想要的行為。例如,下面這個程式,無法透過KeyboardInterrupt來中斷迴圈:
while True:
try:
print('Run it....')
except:
print('exception happened...')
try:
print('Run it....')
except:
print('exception happened...')
由於except捕捉所有的例外,所以上例無法離開迴圈。你可以改為以下的方式:
while True:
try:
print('Run it....')
except Exception:
print('exception happened...')
try:
print('Run it....')
except Exception:
print('exception happened...')
在Python 3中,Exception是BaseException的子類別,可以捕捉除了系統例外以外的所有例外。上例可以藉由 KeyboardInterrupt中斷迴圈。
在Python 3中,可以在except捕捉到例外後,將例外物件指定給變數。例如:
>>> try: ... raise IndexError('11') ... except IndexError as e: ... print(type(e), str(e)) ... <class 'IndexError'> 11 >>> |
在更進階的例外追蹤需求中,可以使用sys.exc_info()方法取得一個Tuple物件,該Tuple物件中包括了例外的類型、例外訊息以及traceback物件:
>>> try: ... raise 'error' ... except: ... a, b, c = sys.exc_info() ... print(a) ... print(b) ... print(c) ... <class 'TypeError'> exceptions must derive from BaseException <traceback object at 0x01D4FB20> >>> |
trackback物件代表了呼叫堆疊中每一個層次的追蹤,可以使用tb_next取得更深一層的呼叫堆疊。例如:
import sys def test(): raise EOFError try: test() except: type, message, traceback = sys.exc_info() while traceback: print('..........') print(type) print(message) print('function or module?', traceback.tb_frame.f_code.co_name) print('file?', traceback.tb_frame.f_code.co_filename) traceback = traceback.tb_next
tb_frame代表了 該層追蹤 的所有物件資訊,f_code可以取得該層的程式碼資訊,例如co_name可取得函式或模組名稱,而co_filename則表示該程式碼所在的檔案。上例的執行範例如下:
.......... <class 'EOFError'> function or module? <module> file? demo.py .......... <class 'EOFError'> function or module? test file? demo.py |
再來看看raise的使用,正如在 try 陳述句 中看到的,你可以在raise後接上字串或例外類別名稱,現在已不鼓勵raise字串實例。實際上,raise之後可以接上例外類別名稱、例外實例或不接上任何東西。
當你在raise後接上例外類別時,實際上會以該類別建立實例再丟出,也就是下面兩行程式碼的作用是相同的:
raise EOFError
raise EOFError()
raise EOFError()
如果在except中使用raise而不接上任何物件,則表示將except比對到的例外實例再度丟出。例如:
>>> try: ... raise EOFError ... except EOFError: ... print('got it') ... raise ... got it Traceback (most recent call last): File "<stdin>", line 2, in <module> EOFError >>> |
如果必要的話,你還可以在except中raise例外時,附上except所比對到的例外實例。例如。
>>> try: ... raise EOFError ... except EOFError as e: ... print('got it') ... raise IndexError from e ... got it Traceback (most recent call last): File "<stdin>", line 2, in <module> EOFError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 5, in <module> IndexError >>> |
raise from語法,會將from後接上的例外實例,設定給被raise的例外實例之__cause__。例如:
>>> try: ... try: ... raise EOFError('XD') ... except EOFError as e: ... print(e.args) ... raise IndexError('Orz') from e ... except IndexError as e: ... print(e.args) ... print(e.__cause__.args) ... ('XD',) ('Orz',) ('XD',) >>> |
實際上,如果你在except中raise某個例外,則原except所比對到的例外,無論有無使用raise from,都會自動設定給__context__。例如:
>>> try: ... try: ... raise EOFError('XD') ... except EOFError as e: ... print(e.args) ... raise IndexError('Orz') from e ... except IndexError as e: ... print(e.args) ... print(e.__cause__.args) ... print(e.__context__.args) ... ('XD',) ('Orz',) ('XD',) ('XD',) >>> |