我們?cè)诰帉?xiě)程序的時(shí)候,經(jīng)常需要對(duì)異常情況做處理。比如,當(dāng)一個(gè)數(shù)試圖除以 0 時(shí),我們需要捕獲這個(gè)異常情況并做處理。你可能會(huì)使用類(lèi)似 if/else 的條件語(yǔ)句來(lái)對(duì)異常情況做判斷,比如,判斷除法的分母是否為零,如果為零,則打印錯(cuò)誤信息。
這在某些簡(jiǎn)單的情況下是可以的,但是,在大多數(shù)時(shí)候,我們應(yīng)該使用 Python 的異常處理機(jī)制。這主要有兩方面的好處:
Python 用異常對(duì)象(exception object)來(lái)表示異常情況。當(dāng)程序在運(yùn)行過(guò)程中遇到錯(cuò)誤時(shí),會(huì)引發(fā)異常。如果異常對(duì)象未被處理或捕捉,程序就會(huì)用所謂的回溯(Traceback,一種錯(cuò)誤信息)終止執(zhí)行。
比如:
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
上面的 ZeroDivisionError 就是一個(gè)異常類(lèi),相應(yīng)的異常對(duì)象就是該類(lèi)的實(shí)例。Python 中所有的異常類(lèi)都是從 BaseException 類(lèi)派生的,常見(jiàn)的異常類(lèi)型可以在這里查看。
在編寫(xiě)程序的時(shí)候,如果我們知道某段代碼可能會(huì)導(dǎo)致某種異常,而又不希望程序以堆棧跟蹤的形式終止,這時(shí)我們可以根據(jù)需要添加 try/except 或者 try/finally 語(yǔ)句(或者它們的組合)進(jìn)行處理。一般來(lái)說(shuō),有以下使用形式:
try...except...
try...except...else...
try...except...else...finally...
try...except...except...else...finally...
try...finally...
捕捉異常的基本形式是 try...except。
讓我們看看第一個(gè)例子:
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e:
print 'Error:',e
print 'hello world'
當(dāng) y = 0 時(shí),看看執(zhí)行結(jié)果:
Enter x: 3
Enter y: 0
Error: integer division or modulo by zero
hello world
可以看到,我們的程序正確捕獲了除以零的異常,而且程序沒(méi)有以堆棧跟蹤的形式終止,而是繼續(xù)執(zhí)行后面的代碼,打印出 'hello world'。
有時(shí),我們的程序可能會(huì)出現(xiàn)多個(gè)異常,這時(shí)可以用多個(gè) except 子句來(lái)處理這種情況。
讓我們繼續(xù)看第一個(gè)例子,如果 y 輸入的是一個(gè)非數(shù)字的值,就會(huì)產(chǎn)生另外一個(gè)異常:
Enter x: 2
Enter y: 'a' # y 的輸入是一個(gè)字符
----------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-209-d4666cfaefb4> in <module>()
2 x = input('Enter x: ')
3 y = input('Enter y: ')
----> 4 print x / y
5 except ZeroDivisionError as e:
6 print e
TypeError: unsupported operand type(s) for /: 'int' and 'str'
可以看到,當(dāng) y 輸入一個(gè)字符 'a' 之后,程序產(chǎn)生了一個(gè) TypeError 異常,并且終止,這是因?yàn)槲覀兊?except 子句只是捕獲了 ZeroDivisionError 異常,為了能捕獲TypeError 異常,我們可以再加一個(gè) except 子句,完整代碼如下:
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e: # 處理 ZeroDivisionError 異常
print 'ZeroDivisionError:',e
except TypeError as e: # 處理 TypeError 異常
print 'TypeError:',e
print 'hello world'
當(dāng) y 輸入 'a' 時(shí),看看執(zhí)行結(jié)果:
Enter x: 3
Enter y: 'a'
TypeError: unsupported operand type(s) for /: 'int' and 'str'
hello world
事實(shí)上,在編寫(xiě)程序的時(shí)候,我們很難預(yù)料到程序的所有異常情況。比如,對(duì)于第一個(gè)例子,我們可以預(yù)料到一個(gè) ZeroDivisionError 異常,如果細(xì)心一點(diǎn),也會(huì)預(yù)料到一個(gè) TypeError 異常,可是,還是有一些其他情況我們沒(méi)有考慮到,比如在輸入 x 的時(shí)候,我們直接按回車(chē)鍵,這時(shí)又會(huì)引發(fā)一個(gè)異常,程序也會(huì)隨之掛掉:
Enter x: # 這里輸入回車(chē)鍵
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<string>", line 0
^
SyntaxError: unexpected EOF while parsing
那么,我們應(yīng)該怎么在程序捕獲某些難以預(yù)料的異常呢?我們?cè)谏衔恼f(shuō)過(guò),Python 中所有的異常類(lèi)都是從 BaseException 類(lèi)派生的,也就是說(shuō),ZeroDivisionError、SyntaxError 等都是它的子類(lèi),因此,對(duì)于某些難以預(yù)料的異常,我們就可以使用 BaseException 來(lái)捕獲,在大部分情況下,我們也可以使用 Exception 來(lái)捕獲,因?yàn)?Exception 是大部分異常的父類(lèi),可以到這里查看所有異常類(lèi)的繼承關(guān)系。
因此,對(duì)于第一個(gè)例子,我們可以把程序做一些修改,使其更加健壯:
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e: # 捕獲 ZeroDivisionError 異常
print 'ZeroDivisionError:',e
except TypeError as e: # 捕獲 TypeError 異常
print 'TypeError:',e
except BaseException as e: # 捕獲其他異常
print 'BaseException:',e
print 'hello world'
注意到,我們把 BaseException 寫(xiě)在了最后一個(gè) except 子句。如果你把它寫(xiě)在了第一個(gè) except 子句,由于 BaseException 是所有異常的父類(lèi),那么程序的所有異常都會(huì)被第一個(gè) except 子句捕獲。
我們可以在 except 子句后面加一個(gè) else 子句。當(dāng)沒(méi)有異常發(fā)生時(shí),會(huì)自動(dòng)執(zhí)行 else 子句。
對(duì)第一個(gè)例子,加入 else 子句:
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e:
print 'ZeroDivisionError:',e
except TypeError as e:
print 'TypeError:',e
except BaseException as e:
print 'BaseException:',e
else:
print 'no error!'
print 'hello world'
看看執(zhí)行結(jié)果:
Enter x: 6
Enter y: 2
3
no error!
hello world
finally 子句不管有沒(méi)有出現(xiàn)異常都會(huì)被執(zhí)行。
看看例子:
try:
x = 1/0
print x
finally:
print 'DONE'
執(zhí)行結(jié)果:
DONE
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
再看一個(gè)例子:
try:
x = 1/0
print x
except ZeroDivisionError as e:
print 'ZeroDivisionError:',e
finally:
print 'DONE'
執(zhí)行結(jié)果:
ZeroDivisionError: integer division or modulo by zero
DONE
有時(shí),我們使用 except 捕獲了異常,又想把異常拋出去,這時(shí)可以使用 raise 語(yǔ)句。
看看例子:
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e:
print 'ZeroDivisionError:',e
except TypeError as e:
print 'TypeError:',e
except BaseException as e:
print 'BaseException:',e
raise # 使用 raise 拋出異常
else:
print 'no error!'
print 'hello world'
運(yùn)行上面代碼,當(dāng) x 輸入一個(gè)回車(chē)鍵時(shí),錯(cuò)誤會(huì)被打印出來(lái),并被拋出:
Enter x: # 這里輸入回車(chē)鍵
BaseException: unexpected EOF while parsing (<string>, line 0)
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<string>", line 0
^
SyntaxError: unexpected EOF while parsing
上面的 raise 語(yǔ)句是不帶參數(shù)的,它會(huì)把當(dāng)前錯(cuò)誤原樣拋出。事實(shí)上,我們也創(chuàng)建自己的異常類(lèi),并拋出自定義的異常。
創(chuàng)建自定義的異常類(lèi)需要從 Exception 類(lèi)繼承,可以間接繼承或直接繼承,也就是可以繼承其他的內(nèi)建異常類(lèi)。比如:
# 自定義異常類(lèi)
class SomeError(Exception):
pass
try:
x = input('Enter x: ')
y = input('Enter y: ')
print x / y
except ZeroDivisionError as e:
print 'ZeroDivisionError:',e
except TypeError as e:
print 'TypeError:',e
except BaseException as e:
print 'BaseException:',e
raise SomeError('invalid value') # 拋出自定義的異常
else:
print 'no error!'
print 'hello world'
運(yùn)行上面代碼,當(dāng) x 輸入一個(gè)回車(chē)鍵時(shí),錯(cuò)誤被打印出來(lái),并拋出我們自定義的異常:
Enter x:
BaseException: unexpected EOF while parsing (<string>, line 0)
----------------------------------------------------------------------
SomeError Traceback (most recent call last)
<ipython-input-20-66060b472f91> in <module>()
12 except BaseException as e:
13 print 'BaseException:',e
---> 14 raise SomeError('invalid value')
15 else:
16 print 'no error!'
SomeError: invalid value
BaseException 類(lèi)派生的。