簡介
當程序發生意外情況時則產生異常。
例如你需要讀一個文件而這個文件並不存在會咋樣?又或者是程序運行時你把它誤刪除了呢?
上述情形通過異常進行處理。
類似的,如果你的程序存在一些非法語句會發生什么呢?這時python會舉手告訴你存在一個錯誤。
錯誤
考慮一個簡單的print函數調用。如果我們把print錯拼成Print(注意大小寫),這時python將引發一個語法錯誤。
>>> Print('Hello World')
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
Print('Hello World')
NameError: name 'Print' is not defined
>>> print('Hello World')
Hello World
我們看到一個NameError被引發並且發生錯誤的位置也被打印出來。這就是一個錯誤處理器(error handler)為這個錯誤所進行的處理。
異常
我們嘗試從用戶讀取輸入,看看當鍵入ctrl-d(注:windows用戶輸入ctrl-z)時會發生什么。
>>> s = input('Enter something --> ')
Enter something -->
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
s = input('Enter something --> ')
EOFError: EOF when reading a line
(注:也許你看到的信息會所有不同,但只要引發了EOFError即可)
可以看到python引發了一個被稱作EOFError的異常,一般這意味着它遇到了一個非期望的文件尾(end of file)符號(注:windows下為ctrl-z)
處理異常
利用try...except語句使得我們可以處理異常。通常我們將語句放入try塊而將錯誤處理放到except塊中。
#!/usr/bin/python
# Filename: try_except.py
try:
text = input('Enter something --> ')
except EOFError:
print('Why did you do an EOF on me?')
except KeyboardInterrupt:
print('You cancelled the operation.')
else:
print('You entered {0}'.format(text))
輸出:
$ python try_except.py
Enter something --> # Press ctrl-d
Why did you do an EOF on me?
$ python try_except.py
Enter something --> # Press ctrl-c
You cancelled the operation.
$ python try_except.py
Enter something --> no exceptions
You entered no exceptions
代碼如何工作:
我們將可能引起異常/錯誤的所有語句放入try塊,然后將適當的錯誤/異常處理器放進except塊/從句中。
except從句可以處理一個單一的指定的錯誤或異常,或者一組括在小括號中的異常/錯誤。
如果沒有給出異常或錯誤,則except會處理所有的錯誤和異常。
注意每個try至少要關聯一個except從句,否則只存在try塊有啥意義呢?
任何沒有被處理的錯誤或異常都會導致python默認處理器的調用,它的作用僅僅是終止程序運行並將錯誤信息打印出來。前面我們已經見識過了。
你還可以為try..except塊關聯一個else從句。如果沒有異常發生則else塊被執行。
下面的例子中,我們將看到如何得到異常對象獲取額外的異常信息。
引發異常
通過raise語句你可以引發異常。為raise語句提供錯誤/異常名后異常對象會被拋出。
你拋出的錯誤或異常必須是一個間接或直接派生自Exception類的類。
#!/usr/bin/python
# Filename: raising.py
class ShortInputException(Exception):
'''A user-defined exception class.'''
def __init__(self, length, atleast):
Exception.__init__(self)
self.length = length
self.atleast = atleast
try:
text = input('Enter something --> ')
if len(text) < 3:
raise ShortInputException(len(text), 3)
# Other work can continue as usual here
except EOFError:
print('Why did you do an EOF on me?')
except ShortInputException as ex:
print('ShortInputException: The input was {0} long, expected at
least {1}'/
.format(ex.length, ex.atleast))
else:
print('No exception was raised.')
輸出:
$ python raising.py
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3
$ python raising.py
Enter something --> abc
No exception was raised.
范例如何工作:
這里,我們創建了自己的異常類型。這個新的異常類型被稱作ShortInputException。
ShortInputException擁有兩個字段 – length指出給定輸入的長度,而atleast為程序希望輸入的最小長度。
在except從句中,我們給定異常類並將其對象存儲為一個變量。這就類似於函數調用中的形參與實參。
在這個特定的except從句中,我們利用異常對象的length和atleast字段向用戶打印出適當的提示信息。
try...finally
假設你的程序正在讀取一個文件。如何保證無論是否發生異常文件對象都能被適當的關閉?這可以通過finally塊做到。
注意你可以同時為try塊關聯except和finally塊。如果你希望同時使用兩者則必須將一個嵌入另一個中。
#!/usr/bin/python
# Filename: finally.py
import time
try:
f = open('poem.txt')
while True: # our usual file-reading idiom
line = f.readline()
if len(line) == 0:
break
print(line, end='')
time.sleep(2) # To make sure it runs for a while
except KeyboardInterrupt:
print('!! You cancelled the reading from the file.')
finally:
f.close()
print('(Cleaning up: Closed the file)')
輸出:
$ python finally.py
Programming is fun
When the work is done
if you wanna make your work also fun:
!! You cancelled the reading from the file.
(Cleaning up: Closed the file)
代碼如何工作:
我們執行一個常見的讀文件操作,但故意在打印每行后利用time.sleep函數讓程序休眠2秒,因此程序會運行的比較慢。
當程序運行時輸入ctrl-c將中斷/取消程序的運行。
注意ctrl-c會導致拋出KeyboardInterrupt異常,隨后程序結束。但在程序結束前finally會被執行因此文件對象永遠都會被關閉。
with語句
在try塊中獲得資源后在finally塊中釋放之是很常見的設計方式。因此python提供with語句給予更簡潔的實現方式。
#!/usr/bin/python
# Filename: using_with.py
with open("poem.txt") as f:
for line in f:
print(line, end='')
代碼如何工作:
程序的輸出應該和上面的范例相同。程序的不同之處在於我們在with語句中使用open函數 – 如此with語句就會自動關閉文件了。
在幕后with與用戶有一個協議。它將讀取open語句返回的對象,這里我們將這個對象稱為”thefile”
在with塊的開始處,with永遠都會調用thefile.__enter__方法而在塊結束處又會調用thefile.__exit__方法。
因此我們在finally塊中的代碼被委托給__exit__方法了。這將幫助我們避免反復的使用try..finally塊。
關於此主題更多的討論已經超出本書范圍,詳見(http://www.python.org/dev/peps/pep-0343/) (注:看本地文檔也行)
小結
我們已經討論了try...except和try...finally的用法。並了解到如何創建自己的異常類型與引發異常。
下面,我們將研究python標准庫。
