異常
異常即非正常狀態,在Python中使用異常對象來表示異常。若程序在編譯或運行過程中發生錯誤,程序的執行過程就會發生改變,拋出異常對象,程序流進入異常處理。如果異常對象沒有被處理或捕捉,程序就會執行回溯(Traceback)來終止程序。
異常類型
通用異常類型表
| 異常 | 描述 |
|---|---|
| BaseException | 所有異常的基類 |
| SystemExit | 解釋器請求退出 |
| KeyboardInterrupt | 用戶中斷執行(通常是輸入^C) |
| Exception | 常規錯誤的基類 |
| StopIteration | 迭代器沒有更多的值 |
| GeneratorExit | 生成器(generator)發生異常來通知退出 |
| StandardError | 所有的內建標准異常的基類 |
| ArithmeticError | 所有數值計算錯誤的基類 |
| FloatingPointError | 浮點計算錯誤 |
| OverflowError | 數值運算超出最大限制 |
| ZeroDivisionError | 除(或取模)零 (所有數據類型) |
| AssertionError | 斷言語句失敗 |
| AttributeError | 對象沒有這個屬性 |
| EOFError | 沒有內建輸入,到達EOF 標記 |
| EnvironmentError | 操作系統錯誤的基類 |
| IOError | 輸入/輸出操作失敗 |
| OSError | 操作系統錯誤 |
| WindowsError | 系統調用失敗 |
| ImportError | 導入模塊/對象失敗 |
| LookupError | 無效數據查詢的基類 |
| IndexError | 序列中沒有此索引(index) |
| KeyError | 映射中沒有這個鍵 |
| MemoryError | 內存溢出錯誤(對於Python 解釋器不是致命的) |
| NameError | 未聲明/初始化對象 (沒有屬性) |
| UnboundLocalError | 訪問未初始化的本地變量 |
| ReferenceError | 弱引用(Weak reference)試圖訪問已經垃圾回收了的對象 |
| RuntimeError | 一般的運行時錯誤 |
| NotImplementedError | 尚未實現的方法 |
| SyntaxError | Python 語法錯誤 |
| IndentationError | 縮進錯誤 |
| TabError | Tab 和空格混用 |
| SystemError | 一般的解釋器系統錯誤 |
| TypeError | 對類型無效的操作 |
| ValueError | 傳入無效的參數 |
| UnicodeError | Unicode 相關的錯誤 |
| UnicodeDecodeError | Unicode 解碼時的錯誤 |
| UnicodeEncodeError | Unicode 編碼時錯誤 |
| UnicodeTranslateError | Unicode 轉換時錯誤 |
| Warning | 警告的基類 |
| DeprecationWarning | 關於被棄用的特征的警告 |
| FutureWarning | 關於構造將來語義會有改變的警告 |
| OverflowWarning | 舊的關於自動提升為長整型(long)的警告 |
| PendingDeprecationWarning | 關於特性將會被廢棄的警告 |
| RuntimeWarning | 可疑的運行時行為(runtime behavior)的警告 |
| SyntaxWarning | 可疑的語法的警告 |
| UserWarning | 用戶代碼生成的警告 |
Exception類:是通用異常基類下列異常類均繼承於Exception類,Python解析器會自動將通用異常類型名稱放在內建命名空間中,所以當使用通用異常類型時,不需要import exceptions模塊。
異常處理
觸發異常raise
raise關鍵字:手動拋出一個通用的異常類型(Exception),類似Java中的throw語句。raise關鍵字后跟異常的名稱,異常名稱能夠標識出異常類的對象。執行raise語句時,python會創建指定異常類的對象,還能夠指定對異常對象進行初始化的參數,參數也可以為由若干參數組成的元組。
注意:一旦執行raise語句,程序就會被終止。
格式:raise [exceptionType[,argument][,traceback]]
def testRaise(number): if number < 1: raise ValueError('Invalid value') #或者 raise ValueError,'Invalid value' testRaise(0)
- 1
- 2
- 3
- 4
- 5
traceback:這個參數用於追蹤異常對象,一般很少使用。
這樣就可以觸發一個異常,並且接收異常信息。
傳遞異常
當你捕獲到異常之后又希望再次的觸發異常只需要使用不帶任何參數的raise關鍵字。
!/usr/bin/env python
import os try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except IOError: print 'File not Exists' if not os.path.exists('notExistsFile.txt'): raise except: print 'process exception'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
異常會在捕獲之后再次觸發同一個異常。
assert語句觸發異常
assert語句根據后面的表達式的真假來控制程序流。若為True,則往下執行。若為False,則中斷程序並調用默認的異常處理器,同時輸出指定的提示信息。
格式:
assert expression,'information'
- 1
Example:
#!/usr/bin/env python def testAssert(x): assert x < 1,'Invalid value' testAssert(1) print 'Valid value'
- 1
- 2
- 3
- 4
- 5
output:
AssertionError: Invaild value
- 1
捕獲異常try..except..else
注意:except子句的數量沒有限制,但使用多個except子句捕獲異常時,如果異常類之間具有繼承關系,則子類應該寫在前面,否則父類將會直接截獲子類異常。放在后面的子類異常也就不會執行。
格式:
try: 可能觸發異常的語句塊 except [exceptionType]: 捕獲可能觸發的異常[可以指定處理的異常類型] except [exceptionType][,date]: 捕獲異常並獲取附加數據 except: 沒有指定異常類型,捕獲任意異常 else: 沒有觸發異常時,執行的語句塊
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
try的工作原理:
執行一個try語句時,python解析器會在當前程序流的上下文中作標記,當出現異常后,程序流能夠根據上下文的標記回到標記位,從而避免終止程序。
1. 如果try語句執行時發生異常,程序流跳回標記位,並向下匹配執行第一個與該異常匹配的except子句,異常處理完后,程序流就通過整個try語句(除非在處理異常時又引發新的異常)。
2. 如果沒有找到與異常匹配的except子句(也可以不指定異常類型或指定同樣異常類型Exception,來捕獲所有異常),異常被遞交到上層的try(若有try嵌套時),甚至會逐層向上提交異常給程序(逐層上升直到能找到匹配的except子句。實在沒有找到時,將結束程序,並打印缺省的錯誤信息)。
3. 如果在try子句執行時沒有發生異常,python將執行else語句后的語句(可選),然后控制流通過整個try語句。
#!/usr/bin/env python try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except IOError: print 'File not Exists' #執行 except: print 'process exception' #不執行 else: print 'Reading the file' #不執行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
output:
In [157]: %run testError.py File not Exists
- 1
- 2
嵌套try:
#!/usr/bin/env python try: try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except IOError: print 'File not Exists' #執行 except: print 'process exception' #不執行 else: print 'Reading the file' #執行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Output:
In [159]: %run testError.py File not Exists Reading the file
- 1
- 2
- 3
捕捉多個異常
方法一:指定一個通用異常,可以捕獲多個不同的包含在Exception類中的異常類。
try: 語句塊 except Exception: 語句塊
- 1
- 2
- 3
- 4
方法二:在一個except子句后將多個異常作為元組元素列出。
try: 語句塊 except (IOError,ValueError): 語句塊
- 1
- 2
- 3
- 4
方法三:except子句后不帶任何異常名稱,捕獲所有異常
try: 語句塊 except: 語句塊
- 1
- 2
- 3
- 4
try..finally語句
無論try語句塊中是否觸發異常,都會執行finally子句中的語句塊,因此一般用於關閉文件或關閉因系統錯誤而無法正常釋放的資源。比如文件關閉,釋放鎖,把數據庫連接返還給連接池等。
import os def write_test(fileName,content_iterable): try: pwd = open(fileName,'w') for key,value in content_iterable.items(): pwd.write(key+'\t'+value+'\n') #傳入String類型參數同時加入換行符 finally: pwd.close() if __name__ == '__main__': fileName = '/usr/local/src/pyScript/fileOperation.txt' dic = {'name':'Jmilk','age':'23','city':'BJ'} if os.path.exists(fileName): write_test(fileName,dic) else:print 'File not exist!'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
注意:try..finally與try..except 是可以同時使用的。
In [3]: try: ...: raise ...: except Exception: ...: print 'error' ...: raise ...: finally: ...: print 'success' ...: error success --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-3-530db52949e7> in <module>() 1 try: ----> 2 raise 3 except Exception: 4 print 'error' 5 raise TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
NOTE:try…finally 的意義在於,就是我們在 try 代碼塊中執行了 return 語句,但是仍然會繼續執行在 finally 中的代碼塊,所以我們一般用作處理資源的釋放。
自定義異常
通過(直接或簡介)繼承Exception類來創建一個自定義異常類,自定義的異常類只能通過raise關鍵字來手動觸發。
class testError(Exception): #直接集成Exception類 def __init__(self,arg): self.args = arg try: raise testError('Just test') except testError,info: print info.args
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output:
In [52]: %run test.py ('J', 'u', 's', 't', ' ', 't', 'e', 's', 't')
- 1
- 2
with..as觸發異常自動關閉資源
在使用類文件的流對象時,都需要單獨的調用close()來關閉資源。with..as語句能夠實現在with語句塊執行完后,自動的關閉文件。如果with語句塊中觸發異常,會調用默認的異常處理器處理,而且文件仍然能夠正常關閉。
#!/usr/bin/env python import os def testWith(fileName): try: with open(fileName,'r+') as pwd: pwd.readlines() print 2/0 except Exception: print 'File closed:',pwd.closed #判斷文件是否關閉 if __name__ == '__main__': if os.path.exists('/usr/local/src/pyScript/fileOperation.txt'): testWith('/usr/local/src/pyScript/fileOperation.txt') print 'continue'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
output:
In [17]: %run test.py File closed: True #沒有call close()函數,文件仍然自動關閉。 continue
- 1
- 2
- 3
as獲取異常信息
每個異常都會有一定的描述信息,可以通過as關鍵字來獲取。但是這種異常信息並不適合一般用戶閱讀,所以會使用自定義的異常信息。但是仍然會將原有的異常信息保留起來,用於后期的異常分析。
#!/usr/bin/env python try: try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except (IOError,ValueError) as info: #或者except (IOError,ValueError),info: print info except: print 'process exception' else: print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
output:
In [164]: %run testError.py [Errno 2] No such file or directory: 'notExistsFile.txt' Reading the file
- 1
- 2
- 3
異常參數
也可以使用異常參數作為輸出的異常信息參數,來獲取異常信息。並且異常參數中包含有異常信息、錯誤數字、錯誤位置等屬性。
#!/usr/bin/env python try: try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except (IOError,ValueError),info: print dir(info) print info.args except: print 'process exception' else: print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
output:
In [44]: %run test.py ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', 'args', 'errno', 'filename', 'message', 'strerror'] (2, 'No such file or directory') Reading the file
- 1
- 2
- 3
- 4
- 5
traceback追蹤異常
使用traceback追蹤異常的時候,需要import traceback模塊。traceback模塊可以有效的幫助查看異常的詳細信息。
注意:若希望獲取異常的詳細信息,卻又不會終止程序的執行,可以在except子句中使用tarceback.print_exc()函數。
tarceback.print_exc():
print_exc(limit=None, file=None)
Shorthand for ‘print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)’.
(In fact, it uses sys.exc_info() to retrieve the same information in a thread-safe way.)
輸出sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file等異常信息,實際上是以線程安全的方式去使用sys.exc_info()函數來獲取相同的信息。
#!/usr/bin/env python import traceback try: openFile = open('notExistsFile.txt','r') fileContent = openFile.readlines() except IOError as info: print 'File not Exists' print info traceback.print_exc() print 'continue' except: print 'process exception' else: print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
output:
In [38]: %run test.py File not Exists [Errno 2] No such file or directory: 'notExistsFile.txt' Traceback (most recent call last): File "/usr/local/src/pyScript/test.py", line 5, in <module> openFile = open('notExistsFile.txt','r') IOError: [Errno 2] No such file or directory: 'notExistsFile.txt' continue
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
異常信息的重定向:如果希望將異常的信息保存在一個指定的文件中,以供后期分析。可以使用下面的方法:
#!/usr/bin/env python import traceback try: with open('notExistsFile.txt','r') as openFile: fileContent = openFile.readlines() except IOError: with open('errorLog','w+') as errorInfo: traceback.print_exc(file=errorInfo) print 'continue' except: print 'process exception' else: print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
output:
In [61]: %run test.py
continue
In [62]: cat errorLog
Traceback (most recent call last): File "/usr/local/src/pyScript/test.py", line 5, in <module> with open('notExistsFile.txt','r') as openFile: IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
sys.exc_info()獲取異常信息
traceback.print_exc()函數實際上是call sys.exc_info()
import sys try: a=b b=c except: info=sys.exc_info() print info[0],":",info[1]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output:
In [65]: %run test.py <type 'exceptions.NameError'> : name 'b' is not defined
- 1
- 2
最后
異常處理用於處理程序錯誤之外,還有許多應用的地方。如關閉資源、平台兼容、模塊導入等。這些使用都是基於對異常處理的實現和機制的理解上,以后我們再一起學習。
