python小白:第二十一天
1、 使用raise引發異常
當程式出現錯誤時,系統會自動引發異常。除此之外,python允許程式自行引發異常,自行引發異常使用raise語句來完成。
1.1 引發異常
很多時候,系統是否要引發異常,可能需要根據應用的業務需求來決定,如果程式中的資料、執行與既定的業務需求不符,這就是一種異常。由於與業務需求不符而產生的異常,必須由程式來決定引發,系統無法引發這種異常。
如果需要在程式中自行引發異常,則應使用raise語句,raise語句有如下三種常用的用法:
raise:單獨乙個raise。該語句引發當前上下文中捕獲的異常(比如在except塊中),或缺省引發runtimeerror異常。
raise異常類:raise後帶乙個異常類。該語句引發指定異常類的預設例項。
raise異常物件:引發指定的異常物件。
上面三種用法最終都是要引發乙個異常例項(即使指定的是異常類,實際上也是引發該類的預設例項),raise語句每次只能引發乙個異常例項。
當python直譯器接收到開發者自行引發的異常時,同樣會中止當前的執行流,跳到該異常對應的except塊,由該except塊來處理該異常。也就是說,不管是系統自動引發的異常,還是程式設計師手動引發的異常,python直譯器對異常的處理沒有任何差別。
即使是使用者自行引發的異常,也可以使用try…except來捕獲它。當然也可以不管它,讓該異常向上(先呼叫者)傳播,如果該異常傳到python直譯器,那麼程式就會中止。
從上面程式可以看到,程式既可以在呼叫mtd(3)時使用try…except來捕獲異常,這樣該異常將會被except 塊捕獲,不會傳播給呼叫它的函式。也可直接呼叫mtd(3),這樣該函式的異常就會直接傳播給它的呼叫函式,如果該函式也不處理該異常,就會導致程式中止。
上面第一行輸出是第一次呼叫mtd(3)結果,該方法引發的異常被except塊捕獲並處理。後面的一大段輸出則是第二次呼叫mtd(3)的結果,由於該異常沒有被except塊捕獲,因此該異常一直向上傳播,直到傳給python直譯器導致程式中止。
第二次呼叫mtd(3)引發的以「file 」開頭的三行輸出,其實顯示的就是異常的傳播軌跡資訊。也就是說,如果程式不對異常進行處理,python缺省會在控制台輸除異常的傳播軌跡資訊。
1.2 自定義異常類
很多時候,程式可選擇引發自定義異常,因為異常的類名通常也包含了該異常的有用資訊。所以在引發異常時,應該選擇合適的異常類,從而可以明確地描述該異常情況。在這種情形下,應用程式常常需要引發自定義異常。
使用者自定義異常都應該繼承exception基類或exception子類,在自定義異常類基本不需要書寫更多的**,只要指定自定義異常類的父類即可。
上面程式建立了auctionexception異常類,該異常類不需要類體定義,因此使用pass語句作為佔位符即可。
在大部分情況下,建立自定義異常類都可採用與auctionexception相似的**來完成,只需改變auctionexception異常的類名即可,讓該異常的類名可以準確地描述該異常。
1.3 except和raise同時使用
在實際應用中對異常可能需要更複雜的處理方式–當乙個異常出現時,單靠某個方法無法完全處理該異常,必須由幾個方法協作才可完全處理該異常。也就是說,在異常出現的當前方法中,程式只對異常進行部分處理,還有些處理需要在該方法的呼叫者中才能完成,所以應該再次引發異常,讓該方法的呼叫者也能捕獲到異常。
為了實現這種通過多個方法協作處理同乙個異常的情形,可以在except塊中結合raise語句來完成。
這種except和raise結合使用的情況在實際應用中非常常用。實際應用對異常的處理通常分成兩個部分:①應用後台需要通過日誌來記錄異常發生的詳細情況;②應用還需要根據異常向應用使用者傳達某種提示。在這種情形下,所有異常都需要兩個方法共同完成,也就必須將except和raise結合使用。
如果程式需要將原始異常的詳細資訊直接傳播出去python也允許用自定義異常對原始異常進行包裝,只要將**改為如下形式:
上面就是把原始異常e包裝成了auctionexception異常,這種方式也被稱為異常包裝或異常轉譯。
1.4 raise不需要引數
在使用raise語句時可以不帶引數,此時raise語句處於except塊中,它將會自動引發當前上下文啟用的異常,否則,通常預設引發runtimeerror異常。
2、 python 的異常傳播軌跡
異常物件提供了乙個with_traceback用於處理異常的傳播軌跡,檢視異常的傳播軌跡可追蹤異常觸發的源頭,也可看到異常一路觸發的軌跡。
異常從thirdmethod()函式開始觸發,傳到secondmethod()函式,再傳到firstmethod()函式,最後傳到main()函式,在main()函式止,這個過程就是python異常傳播軌跡。
在實際應用程式的開發中,大多數複雜操作都會被分解成一系列函式或方法呼叫。這是因為為了具有更好的可重用性,會將每個可重用的**單元定義成函式或方法,將複雜任務逐漸分解為更易管理的小型子任務。由於乙個大的業務功能需要由多個函式或方法來共同實現,在最終程式設計模型中,很多物件將通過一系列函式或方法呼叫來實現通訊,執行任務。
所以,當應用程式執行時,經常會發生一系列函式或方法呼叫,從而形成「函式呼叫棧」。異常的傳播則相反,只要異常沒有被完全捕獲(包括異常沒有**獲,或者異常被處理後重新引發了新異常),異常就從發生異常的函式或方法逐漸向外傳播,首先傳給該函式或方法的呼叫者,該函式或方法的呼叫者再傳給其呼叫者……直至最後傳到python直譯器,此時python直譯器會中止該程式,並列印異常的傳播軌跡資訊。
python專門提供了traceback模組來處理異常傳播軌跡,使用traceback可以方便地處理python的異常傳播軌跡。匯入traceback模組之後,traceback提供了如下兩個常用方法。
traceback.print_exc():將異常傳播軌跡資訊輸出到控制台或指定檔案中。
format_exc():將異常傳播軌跡資訊轉換成字串。
從上面方法看不出它們到底處理哪個異常的傳播軌跡資訊。實際上我們常用的print_exc()是print_exc([limit[,file]])省略了limit、file兩個引數的形式。
而print_exc([limit[,file]])的完整形式是print_exception(etype,value,tb[,limit[,file]]),在完整形式中,前面三個引數用於分別指定異常的如下資訊。
etype:指定異常型別。
value:指定異常值。
tb:指定異常的traceback資訊。
當程式處於except塊中時,該except塊所捕獲的異常資訊可通過sys物件來獲取,其中sys.exc_type、sys.exc_value、sys.exc_traceback就代表當前except塊內的異常型別、異常值和異常傳播軌跡。
簡單來說,print_exc([limit[,file]])相當於如下形式:
也就是說,使用print_exc([limit[,file]])會自動處理當前except塊所捕獲的異常。該方法還涉及兩個引數。
limit:用於限制顯示異常傳播的層數,比如函式a呼叫函式b,函式b發生了異常,如果指定limit=1,則只顯示函式a裡面發生的異常,如果不設定limit引數,則預設全部顯示。
file:指定將異常傳播軌跡資訊輸出到指定檔案中。如果不指定該引數,則預設輸出到控制台。
借助於traceback模組的幫助,我們可以使用except塊捕獲異常,並在其中列印異常傳播資訊,包括把它輸出到檔案中。
上面程式第一行先導入了traceback模組,接下來程式使用except捕獲程式的異常,並使用traceback.print_exc()方法輸出異常傳播資訊,分別將它輸出到控制台和指定檔案中。執行上面程式,同樣可以看到在控制台輸出異常傳播資訊,而且在程式目錄下生成了乙個log.txt檔案,該檔案中同樣記錄了異常傳播資訊。
3、 異常處理規則
成功的異常處理應該實現如下四個目標。
使程式**混亂最小化。
捕獲並保留診斷資訊。
通知合適的人員。
採用合適的方式結束異常活動。
3.1 不要過度使用異常
過度使用異常主要表現在兩個方面。
把異常和普通錯誤混淆在一起,不再編寫任何錯誤處理**,而是以簡單地引發異常來代替所有的錯誤處理。
使用異常處理來代替流程控制
熟悉了異常使用方法後,程式設計師可能不再願意編寫煩瑣的錯誤處理**,而是簡單地引發異常。實際上這樣做是不對的,對於完全己知的錯誤和普通的錯誤,應該編寫處理這種錯誤的**,增加程式的健壯性。只有對於外部的、不能確定和預知的執行時錯誤才使用異常。
必須指出:異常處理機制的初衷是將不可預期異常的處理**和正常的業務邏輯處理**分離,因此絕不要使用異常處理來代替正常的業務邏輯判斷。
另外,異常機制的效率比正常的流程控制效率差,所以不要使用異常處理來代替正常的程式流程控制。
執行上面程式確實可以實現遍歷my_list列表的功能,但這種寫法可讀性較差,而且執行效率也不高,程式完全有能力避免產生indexerror異常,程式「故意」製造這種異常,然後使用except塊去捕獲該異常,這是不應該的。
異常只應該用於處理非正常的情況,不要使用異常處理來代替正常的流程控制。對於一些完全可預知,而且處理方式清楚的錯誤,程式應該提供相應的錯誤處理**,而不是將其籠統地稱為異常。
3.2 不要使用過於龐大的try塊
正確的做法是,把大塊的try塊分割成多個可能出現異常的程式段落,並把它們放在單獨的try塊中,從而分別捕獲並處理異常。
3.3 不要忽略捕獲到的異常
不要忽略異常!既然己捕獲到異常,那麼except塊理應做些有用的事情,處理並修復異常。except整個為空,或者僅僅列印簡單的異常資訊都是不妥的!
通常建議對異常採取適當措施,如:
處理異常。對異常進行合適的修復,然後繞過異常發生的地方繼續執行;或者用別的資料進行計算,以代替期望的方法返回值;或者提示使用者重新操作……總之,程式應該盡量修復異常,使程式能恢復執行。
重新引發新異常。把在當前執行環境下能做的事情盡量做完,然後進行異常轉譯,把異常包裝成當前層的異常,重新傳給上層呼叫者。
在合適的層處理異常。如果當前層不清楚如何處理異常,就不要在當前層使用except語句來捕獲該異常,讓上層呼叫者來負責處理該異常。
第二十一天
好久沒有寫點東西了。或許是為了忙個經濟師中級的考試,列印了很多的資料,每章一節一節地在消化,咀嚼著生硬的專業術語,那滋味,只有體會過了才知道。乙個晚上看下來,做夢也是這些內容,有點累,但是,為了目標,只有克服,努力前進。單位的專案要啟動了,事情特別多,給我們準備的時間就乙個禮拜,一開始拿到這個內容需...
第二十一天 PYTHON學習
今日學習 一 什麼是序列化?我們把物件 變數 從記憶體中變成可儲存或傳輸的過程稱之為序列化,在python中叫pickling,在其他語言中也被稱之為serialization,marshalling,flattening等等,都是乙個意思。二 為什麼要序列化?1 持久儲存狀態 硬碟存 需知乙個軟體...
第二十一天總結
今天學習了c 中的函式,感覺好難看懂,跟數學含糊一樣難,函式是一組一起執行乙個任務的語句。每個 c 程式都至少有乙個函式,即主函式 main 所有簡單的程式都可以定義其他額外 的函式。您可以把 劃分到不同的函式中。如何劃分 到不同的函式中是由您來決定的,但在邏輯上,劃分通常是根據每個函式執行乙個特定...