Python的異常處理能力是很強大的,可向使用者準確反饋出錯資訊。在Python中,異常也是對象,可對它進行操作。所有異常都是基類Exception的成員,所有異常都從基類Exception繼承,而且都在exceptions模組中定義,Python自動將所有異常名稱放在內建命名空間中,所以程式不必匯入exceptions模組即可使用異常。
一旦引發而且沒有捕捉SystemExit異常,程式執行就會終止。如果互動式會話遇到一個未被捕捉的SystemExit異常,會話就會終止。
一、異常的捕獲
異常的捕獲有以下幾種方法:
1:使用try和except語句
代碼如下 |
複製代碼 |
try: block except [exception,[data…]]: block try: block except [exception,[data...]]: block else: block |
該種異常處理文法的規則是:
• 執行try下的語句,如果引發異常,則執行過程會跳到第一個except語句。
• 如果第一個except中定義的異常與引發的異常匹配,則執行該except中的語句。
• 如果引發的異常不匹配第一個except,則會搜尋第二個except,允許編寫的except數量沒有限制。
• 如果所有的except都不匹配,則異常會傳遞到下一個調用本代碼的最高層try代碼中。
• 如果沒有發生異常,則執行else塊代碼。
範例程式碼:
代碼如下 |
複製代碼 |
try: f = open(“file.txt”,”r”) except IOError, e: print e |
捕獲到的IOError錯誤的詳細原因會被放置在對象e中,然後運行該異常的except代碼塊,也可以使用以下方法來捕獲所有的異常:
代碼如下 |
複製代碼 |
try: a=b b=c except Exception,ex: print Exception,":",ex |
使用except子句需要注意的事情,就是多個except子句截獲異常時,如果各個異常類之間具有繼承關係,則子類應該寫在前面,否則父類將會直接截獲子類異常,放在後面的子類異常也就不會執行到了。
2:使用try跟finally
代碼如下 |
複製代碼 |
try: block finally: block |
該語句的執行規則是:
• 執行try下的代碼。
• 如果發生異常,在該異常傳遞到下一級try時,執行finally中的代碼。
• 如果沒有發生異常,則執行finally中的代碼。
第二種try文法在無論有沒有發生異常都要執行代碼的情況下是很有用的,例如我們在python中開啟一個檔案進行讀寫操作,我在操作過程中不管是否出現異常,最終都是要把該檔案關閉的。
這兩種形式相互衝突,使用了一種就不允許使用另一種,而功能又各異。
二、手工引發引發一個異常
在Python中,要想引發異常,最簡單的形式就是輸入關鍵字raise,後跟要引發的異常的名稱。異常名稱標識出具體的類:Python異常是那些類的對象,執行raise語句時,Python會建立指定的異常類的一個對象,raise語句還可指定對異常對象進行初始化的參數,為此,請在異常類的名稱後添加一個逗號以及指定的參數(或者由參數構成的一個元組)。
範例程式碼:
代碼如下 |
複製代碼 |
try: raise MyError #自己拋出一個異常 except MyError: print 'a error' raise ValueError,'invalid argument' |
捕捉到的內容為:
代碼如下 |
複製代碼 |
type = VauleError message = invalid argument |
三、跟蹤查看異常
發生異常時,Python能“記住”引發的異常以及程式的目前狀態,Python還維護著traceback(跟蹤)對象,其中含有異常發生時與函數呼叫堆疊有關的資訊,異常可能在一系列嵌套較深的函數調用中引發,程式調用每個函數時,Python會在“函數呼叫堆疊”的起始處插入函數名,一旦異常被引發,Python會搜尋一個相應的例外處理常式。
如果當前函數中沒有例外處理常式,當前函數會終止執行,Python會搜尋當前函數的調用函數,並以此類推,直到發現匹配的例外處理常式,或者Python抵達主程式為止,這一尋找合適的例外處理常式的過程就稱為“堆棧輾轉開解”(Stack Unwinding)。解譯器一方面維護著與放置堆棧中的函數有關的資訊,另一方面也維護著與已從堆棧中“輾轉開解”的函數有關的資訊。
代碼如下 |
複製代碼 |
try: block except: traceback.print_exc() |
四、採用sys模組回溯最後的異常
代碼如下 |
複製代碼 |
import sys try: block except: info=sys.exc_info() print info[0],":",info[1] |
或者以如下的形式:
代碼如下 |
複製代碼 |
import sys tp,val,td = sys.exc_info() |
sys.exc_info()的傳回值是一個tuple, (type, value/message, traceback)
這裡的type是異常的類型,value/message是異常的資訊或者參數,traceback包含調用棧資訊的對象,從這點上可以看出此方法涵蓋了traceback。
以上都是錯誤處理的理論知識,接下來我們要動手設計一個自己的異常處理類,用來記錄異常日誌,將錯誤的日誌按照每小時一個檔案的頻率,儲存到我們指定的位置。代碼如下:
代碼如下 |
複製代碼 |
#coding:utf-8 #基於python2.6 import logging,os,time,traceback class LOG: def __init__(self,logger): self.fileHandlerName = '' self.fileHandler = None self.loggerName = logger self.logger = logging.getLogger(logger) self.logger.setLevel(logging.DEBUG) self.formatter = logging.Formatter("=========================ntime:%(asctime)s nlogger:%(name)s nlevel:%(levelname)s nfile:%(filename)s nfun:%(funcName)s nlineno:%(lineno)d nmessage:%(message)s") # 控制台 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) ch.setFormatter(self.formatter) self.logger.addHandler(ch) path = os.path.abspath(os.path.dirname(__file__)) + '/log/'+self.loggerName+'/' print 'log path=',path def setfh(self): fname = time.strftime("%Y%m%d%H") if fname!=self.fileHandlerName: #移除原來的控制代碼 if self.fileHandler!=None : self.logger.removeHandler(self.fileHandler) #設定記錄檔儲存位置 path = os.path.abspath(os.path.dirname(__file__)) + '/log/'+self.loggerName+'/' print path if os.path.isdir(path) == False: os.makedirs(path) fh = logging.FileHandler(path+fname+'.log') fh.setLevel(logging.DEBUG) fh.setFormatter(self.formatter) self.logger.addHandler(fh) self.fileHandlerName = fname self.fileHandler = fh #格式化日誌內容 def _fmtInfo(self,msg): if len(msg)==0: msg = traceback.format_exc() return msg else: _tmp = [msg[0]] _tmp.append(traceback.format_exc()) return 'n**********n'.join(_tmp) #封裝方法 def debug(self,*msg): _info = self._fmtInfo(msg) try: self.setfh() self.logger.debug(_info) except: print 'mylog debug:' + _info def error(self,*msg): _info = self._fmtInfo(msg) try: self.setfh() self.logger.error(_info) except: print 'mylog error:' + _info def info(self,*msg): _info = self._fmtInfo(msg) try: self.setfh() self.logger.error(_info) except: print 'mylog info:' + _info def warning(self,*msg): _info = self._fmtInfo(msg) try: self.setfh() self.logger.error(_info) except: print 'mylog warning:' + _info if __name__=='__main__': log = LOG('fight') try: print 1/0 except: log.error() #使用系統自己的錯誤描述 try: print 2/0 except: log.error('搞錯了,分母不能為0') |
#使用自己的錯誤描述
運行一下,我們會在該檔案目錄下的log/fight下看到一個記錄檔