1. 程式人生 > >Python異常捕獲與處理

Python異常捕獲與處理

轉載自: JmilkFan:http://blog.csdn.net/jmilk

異常

異常即非正常狀態,在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)
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'
異常會在捕獲之後再次觸發同一個異常。

assert語句觸發異常

assert語句根據後面的表示式的真假來控制程式流。若為True,則往下執行。若為False,則中斷程式並呼叫預設的異常處理器,同時輸出指定的提示資訊。 
格式

assert expression,'information'
Example:
#!/usr/bin/env python
def testAssert(x):
    assert x < 1,'Invalid value'
testAssert(1)
print 'Valid value'
output:
AssertionError: Invaild value

捕獲異常try..except..else

注意:except子句的數量沒有限制,但使用多個except子句捕獲異常時,如果異常類之間具有繼承關係,則子類應該寫在前面,否則父類將會直接截獲子類異常。放在後面的子類異常也就不會執行。 
格式

try:
    可能觸發異常的語句塊
except [exceptionType]:
    捕獲可能觸發的異常[可以指定處理的異常型別]
except [exceptionType][,date]:
    捕獲異常並獲取附加資料
except:
    沒有指定異常型別,捕獲任意異常
else:
    沒有觸發異常時,執行的語句塊
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'       #不執行
output
In [157]: %run testError.py
File not Exists
巢狀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'         #執行
Output
In [159]: %run testError.py
File not Exists
Reading the file
捕捉多個異常

方法一:指定一個通用異常,可以捕獲多個不同的包含在Exception類中的異常類

try:
    語句塊
except Exception:
    語句塊
方法二:在一個except子句後將多個異常作為元組元素列出
try:
    語句塊
except (IOError,ValueError): 
    語句塊
方法三:except子句後不帶任何異常名稱,捕獲所有異常
try:
    語句塊
except: 
    語句塊
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!'
注意: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
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
output
In [52]: %run test.py
('J', 'u', 's', 't', ' ', 't', 'e', 's', 't')
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'
output
In [17]: %run test.py
File closed: True    #沒有call close()函式,檔案仍然自動關閉。
continue
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'
output
In [164]: %run testError.py
[Errno 2] No such file or directory: 'notExistsFile.txt'
Reading the file
異常引數

也可以使用異常引數作為輸出的異常資訊引數,來獲取異常資訊。並且異常引數中包含有異常資訊、錯誤數字、錯誤位置等屬性

#!/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'
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
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'
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
異常資訊的重定向:如果希望將異常的資訊儲存在一個指定的檔案中,以供後期分析。可以使用下面的方法
#!/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'
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'
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]  
output
In [65]: %run test.py
<type 'exceptions.NameError'> : name 'b' is not defined
最後

異常處理用於處理程式錯誤之外,還有許多應用的地方。如關閉資源、平臺相容、模組匯入等。這些使用都是基於對異常處理的實現和機制的理解上,以後我們再一起學習。