python的學習(十二)—-異常處理(3)

來源:互聯網
上載者:User

異常處理(3)

一、異常基礎
try/except:捕捉由代碼中的異常並恢複,匹配except裡面的錯誤,並自行except中定義的代碼,後繼續執行程式(發生異常後,由except捕捉到異常後,不會中斷程式,繼續執行try語句後面的程式)
try/finally: 無論異常是否發生,都執行清理行為 (發生異常時程式會中斷程式,只不過會執行finally後的代碼)
raise: 手動在代碼中接觸發異常。
assert: 有條件地在程式碼中觸發異常。
with/as 在Python2.6和後續版本中實現環境管理器。
使用者定義的異常要寫成類的執行個體,而不是字串、。
finally可以和except和else分句出現在相同的try語句內、
1、異常的角色
錯誤處理
事件通知
特殊情況處理:有時發生很罕見的情況,很難調整代碼去處理。通常會在異常處理器中處理這些罕見的情況,從而省去編寫應對特殊情況的代碼
終止行為
非常規控制流程程

>>> x='diege>>> def fetcher(obj,index):...     return obj[index]...>>> fetcher(x,4)'e'>>> fetcher(x,5)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<stdin>", line 2, in fetcherIndexError: string index out of range>>> try:...     fetcher(x,5)        #嘗試抓取第5個字元... except IndexError:      #如果發生異常【指出引發的異常名稱】...     print fetcher(x,-1)  #那就抓取最後一個字元...e>>> def catcher():...     try:...             fetcher(x,5)...     except IndexError:...             print fetcher(x,-1)...     print "continuing"...>>> catcher()    econtinuing

可以看到從異常中恢複並繼續執行。
try/finally: 無論異常是否發生,都執行清理行為(發生異常時程式也會終端,只不過會執行finally後的代碼)

>>> try:...     fetcher(x,4)... finally:...     print 'after fetch'...'e'after fetch

沒有發生異常的情況,也執行finally語句中的代碼
發生異常的情況下

>>> try:...     fetcher(x,5)      ... finally:              ...     print 'after fetch'...after fetchTraceback (most recent call last):  File "<stdin>", line 2, in <module>  File "<stdin>", line 2, in fetcherIndexError: string index out of range

發生異常的情況下,也執行了finally語句中的代碼
實際應用鎮南關,try/except的組合可用於捕捉異常並從中恢複,而try/finally的組合則很方便,可以確保無論try代碼塊內的
代碼是否發生異常,終止行為一定會運行。如,try/except來捕捉第三方庫匯入的代碼所引發的錯誤,然後以try/finally來確保
關閉檔案,或者終止伺服器串連等調用。
可以在同一個try語句內混合except和finally分句:finally一定回執行,無論是否有異常引發,而且不也不關異常是否被except分句捕捉到
2、try/except/else語句
try的完整形式:try/多個except/else語句
else是可選的
try首行底下的代碼塊代表此語句的主要動作:試著執行的程式碼。except分句定義try代碼塊內引發的異常處理器,而else分句(如果有)則是提供沒有發生異常時候要執行的處理器。
*如果try代碼塊語句執行時發生了異常,Python就跳回try,執行第一個符合引發的異常的except分句下面的語句。當except代碼執行後(除非 except代碼塊引發另一異常),控制全就會到整個try語句後繼續執行。
*如果異常發生在try代碼塊內,沒有符合的except分句,異常就會向上傳遞到程式的之前進入try中,或者到這個進程的頂層(使用Python終止這個程式並列印預設的錯誤訊息)
*如果try首行底下執行的語句沒有發生異常,Python就會執行else行下的語句(如果有的話),控制權會在整個try語句下繼續。
也就是說except分句會捕捉try代碼塊執行時所有發生的任何異常,而else分句只在try代碼執行沒有發生異常時才執行,finally分句無法釋放發生異常都執行。
3、try語句分句形式
分句形式            說明
except:                捕捉所有(其他)異常類型
except name:        只捕捉特定的異常
except name,value:    捕捉所有的異常和其額外的資料(或執行個體)
except (name1,name2) 捕捉任何列出的異常
except (name1,name2),value: 捕捉任何列出的異常,並取得其額外資料
else:                如果沒有引發異常,就運行
finally:            總是會運行此代碼塊,無論是否發生異常
except:用在想不到異常情況,在except:前面可以定義可以想到的異常:except name1: except name2:
3、try/else分句

        try:                customer=getargv[1]        except IndexError:                print "please give argv as customer!"        else:                scene=PizzaShop()                scene.order(customer)                print '...'

如把else:語句的內容放到try:中,也能執行。類比出else分句。不過這可能造成不正確的異常分類。如果“沒有異常發生”這個行為觸發了
IndexError,就會視為try代碼塊的失敗,因此錯誤地觸發try底下的異常處理器。改為明確的else分句,讓邏輯封明確。保證except處理器
只會因為封裝在try中代碼真正的失敗而執行,而不是為else中的情況行為失敗而執行。
4、try/finally分句
python先運行try: 下的代碼塊:
如果try代碼塊運行時沒有異常發生,Python會跳至finally代碼塊。然後整個try語句後繼續執行下去。
如果try代碼塊運行時有發生異常,Python依然會回來運行finally代碼塊,但是接著會把異常向上傳遞到較高的try語句或頂層的預設處理器。程式不會在try語句繼續執行。

        try:                Uppercase(open('/etc/rc.conf'),output).process()        finally:                open('/etc/rc.conf').close

5、統一try/except/finally分句

try:    main-action:except Exception1:    hander1except Exception2:    hander2...else:    else-blockfinally:    finally-block

這語句中main-action代碼會先執行。如果該程式碼(main-action)引發異常,那麼except代碼塊都會逐一測試,尋找與拋出的異常相符的語句。如果引發異常的是Exception1則會執行hander1代碼塊,如果引發異常的是Exception2,則會執行hander2代碼塊。以此類推。如果沒有
引發異常,將會執行else-block代碼塊。
無論前面發生什麼,當main-action代碼塊完成時。finally-block都會執行。
6、通過嵌套合并except和finally

try:    try:        main-action:    except Exception1:        hander1    except Exception2:        hander2    ...    else:        else-blockfinally:    finally-block

和5的效果一樣
7、raise語句
要故意觸發異常,可以使用raise語句。raise語句組成是: raise關鍵字,後面跟著要引發的異常名稱(選用),以及一個可選的額外的資料項目,後可隨著異常傳遞
raise <name>
raise <name>,<data>
raise
注意:<name>需要預先定義好,不然會由未定義錯誤。
第二種形式隨著異常傳遞額外的資料項目,在raise語句中,資料是列在異常名稱的後面的;在try語句中,取得該資料是通過引入一個進行接收
它的變數實現的。例如,如果try引入一個exceptname,X:語句,則變數X就會被賦值為raise內所列出的額外的資料項目,如果沒有定義預設接受到
的就是特殊對象None。一旦被程式中任意的except分句捕捉,異常就死了(也就是說,不會傳遞給另一個try),除非又被另一個raise語句或
錯誤所引發。現在使用者定義的異常應該是類執行個體對象。
8、assert語句
assert可以有條件地在程式碼中觸發異常,可以認為是有條件的raise.
牢記:assert幾乎都是用來收集使用者定義的約束條件,而不是捕捉內在的程式設計錯誤。因為Python會自動收集程式的設計錯誤,通常咩有必要寫assert去捕捉超出索引值,類型不符以及除數為0之類的事。
引發的異常為:AssertionError。如果沒有被try捕捉到,就會終止程式。
該語句形式:
assert  <test>,<data>
執行個體

>>> def f(x):...     assert x>0,'x must be great zerot'...     return x**2...>>> f(2)    4>>> f(-1)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<stdin>", line 2, in fAssertionError: x must be great zerot

二、with/as環境管理
python2.6引入新的異常相關的語句:with及其可選的as分句。這個語句的設計是為了和環境管理器對象(支援新的方法協議)一起工作。
簡而言之, with/as語句的設計作為常見try/finally用法模式的替代方案。就像try/finally語句, with/as語句也用於定義必須執行的
終止或“清理"行為,無論步驟中是否發生異常。和try/finally不同的是,with語句支援更豐富的基於對象的協議,可以代碼塊定義支援進入
和離開動作。
with語句基本格式:
with expression [as variable]:
    with block
在這裡expression要返回一個對象,從而支援環境管理協議。如果選用as分句存在時,此對象也可返回一個值,賦值給變數名variable.
注意:variable並非賦值為expression的結果,expression的結果是支援環境協議的對象,而variable則是賦值為其他的東西(??)
然後,expression返回的對象可在with-block開始前,先自行啟動程式,並且在該代碼塊完成後,執行終止程式碼,無論代碼塊是否引發異常
有些內建的Python對象已得到強化,支援環境管理協議,因此可以用於with語句。例如,檔案對象有環境管理器,可在with代碼塊後自動關閉
檔案,無法是否引發異常。
>>> with open('/etc/rc.conf') as myfile:
...     for line in myfile:
...             line=line.upper()
...             print line
在這裡,對open的調用,會返回一個簡單檔案對象,賦值給變數名myfile。我們可以用一般的檔案工具使用myfile:就此而言,檔案迭代器會在
for迴圈內逐行讀取。
然後,此對象也支援with語句所使用的環境協議。在這個with語句執行後。環境管理機制保證由myfile所引用的檔案對象自動關閉。即時處理
該檔案時,for迴圈引發了異常。
環境管理器是有寫進階的機制。還不是Python的正式組成部分。就較為簡單的用途來說,try/finally語句可對終止活動提供足夠的支援。

三、異常對象
基於類的異常可以建立各種異常類,有附加狀態資訊,而且支援繼承。盡量都適用類異常。類異常有如下特點;
* 提供類型分類,對今後的修改有更好的支援:以後增加新異常時,通常不需要在try語句中進行修改。
* 同了儲存在try處理器中所使用的環境資訊的合理地點:這樣的話,可以擁有狀態資訊,以及可調用的方法,並且可通過執行個體進行讀取。
* 允許異常參與繼承層次,從而可獲得共同的行為。例如,繼承的顯示方法可提供通用的錯誤訊息外觀。
所有內建異常都類組織成繼承樹。
Python2.5版本 字串異常會產生'deprecation‘(不建議使用)’警告。python3.0將不再支援字串異常。
1、基於字串的異常

>>> myexc="My exception string">>> try:...     raise myexc... except myexc:...     print "caught"...Traceback (most recent call last):  File "<stdin>", line 2, in <module>TypeError: exceptions must be old-style classes or derived from BaseException, not str

Python 2.7版本已經不在支援了
2、基於類的異常

>>> class General:pass>>> class Spec1(General):pass >>> class Spec2(General):pass>>> def raiseer0():...     X=General()...     raise X>>> def raiseer1():...     X=Spec1()      ...     raise X>>> def raiseer2():...     X=Spec2()  ...     raise X>>> for func in (raiseer0,raiseer1,raiseer2):...     try:...             func()...     except General:#使用異常的超類General,這樣子類也捕捉到,可以在未來增加函數異常(在子類裡),而不影響程式。...             import sys...             print 'caught:',sys.exc_info()[0]...caught:__main__.Generalcaught:__main__.Spec1caught:__main__.Spec2

在try語句中,捕捉其超類就會捕捉這個類,以及類樹中超類下的所有子類:超類會變成異常分類的名稱,而子類會變成該分類中特定的
異常類型。
Python2.5以後版本將每個異常都寫成類(必須),從異常樹頂層繼承Exception(非必須)。
sys.exc_info() 一種抓取最近發生異常的常用方式。
對基於類的異常而言,其結果中第一個元素就是引發異常類,而第二個是實際引發的執行個體。
注意:目前Python的說明檔案指出,使用者定義的異常最好繼承自Exception內建的異常(但不是必須要求)。
class General(Exception):pass
就大型多層次的異常而言,在一個except分句使用類捕捉分類,會比列出一個分類中每個成員更簡單。新增子類擴充異常層次,。也不會破壞
先有的代碼。
如果不用類,在excpet中就會是excpet (General,Spec1,Spec2): 這樣捕捉字串類。
基本原則是:在異常處理器中,通常來說具體要優於一般。
3、內建Exception類
Python把內建異常組織成層次,來支援各種捕捉模式
Exception:    異常的頂層根超類
StandardError:    所有內建錯誤異常的超類
ArithmeticError:    所有數值錯誤的超類
OverflowError:    識別特定的數值錯誤的子類
可以在Python庫手冊或exceptionsn模組的協助文本中查閱。
>>> import exceptions
>>> help(exceptions)  
BaseException
            Exception
                StandardError
                    ArithmeticError
                        FloatingPointError
                        OverflowError
                        ZeroDivisionError
                    AssertionError
4、定義異常文本
對基於類的異常而言,其結果中第一個元素就是引發異常類,而第二個是實際引發的執行個體。
>>> raise MyBad():
>>> raise MyBad()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyBad: <__main__.MyBad instance at 0x2850d26c>
這樣的顯示不友好。改進顯示,可以在類中定義__repr__或__str__顯示字串重載方法,從而返回異常達到想要預設處理器顯示字串。
>>> class MyBad():                        
...     def __repr__(self):               
...             return "Sorry--my mistake!"
...
>>> raise MyBad()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyBad: Sorry--my mistake
這樣把顯示類的執行個體改為了我們定義的文本。
注意:如果繼承自內建異常類,錯誤測試會有細微的改變,構造方法參數會自動儲存並顯示在訊息中。【同樣也可以把繼承的重載】
>>> class MyBad(Exception):pass
... >>> raise MyBad()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyBad
>>> raise MyBad('the','bright','side','of')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyBad: ('the', 'bright', 'side', 'of')
5、發送額外資料和執行個體行為
把環境資訊附加在基於類的異常的辦法是:在引發的執行個體對象中填寫執行個體的屬性,通常是在類的構造器方法中。在異常處理器中,是列出
要賦值為引發的執行個體的變數,然後通過這個變數名來讀取附加的轉改資訊,並且調用任何基礎的類方法。【很強大的功能】

>>> class FormatError:...     def __init__(self,line,file):...             self.line=line...             self.file=file>>> def parser():...     raise FormatError(42,file='diege.txt') #手動定義異常,基於類的異常,類建構函式傳遞兩個資料。...>>> try:...     parser()... except FormatError,X: #定義接受異常(類的執行個體-異常引發時產生的執行個體)傳遞過來資料的變數。...     print 'Error at',X.file,X.line #顯示執行個體傳遞過來的資料...Error at diege.txt 42

字串的格式為【已淘汰】
raise formatError,{'file':'diege.txt','line':43}
except FormatError,X:
    print 'Error at',X['file'],X['line']
6、raise的一般形式
raise string #基於字串的異常,已淘汰
raise string,data #基於字串的異常,已淘汰
raise instance    #最常用的模式,直接接一個執行個體:raise FormatError(42,file='diege.txt')
raise class,instance
raise
為了和內建異常為字串的的舊版相容,也可以
raise class #same as :raise class()
raise class,arg # same as :raise class(arg)
raise clase(arg1,arg2,...) #same as:raise class(arg1,arg2...)
這些都相當於raise class(arg),等效於raise instance形式
>>> def parse():
...     raise FormatError,(42,'diege.txt')

四、異常的設計
1、嵌套異常處理器
把內部的try寫成函數來嵌套

>>> def action2():...     print 1+[]>>> def action1():...     try:...             action2()...     except TypeError:...             print "inner try">>> try:...     action1()... except TypeError:...     print "outer try"...inner try使用文法嵌套>>> try:...     try:...             action2()...     except TypeError:...             print "inner try"  ... except TypeError:...     print "outer try"...inner try

嵌套的finally語句會因一個異常全部啟動。

>>> try:   ...     try:...             raise IndexError...     finally:            ...             print "diege"... finally:     ...     print "DIEGE"...diegeDIEGETraceback (most recent call last):  File "<stdin>", line 3, in <module>IndexError

2、異常的習慣使用者
1)異常不總是錯誤
在Python中,所有的錯誤都是異常。但並非所有的異常都是錯誤。

>>> while 1:...     try:...             line=raw_input()...     except EOFError:...             break...     else:...             print "print ...process nex line here.."

raw_input()在檔案末尾引發內建的EOFError
2)函數訊號條件和raise
使用者定義的異常也可以引發非錯誤的情況。
用於任何無法返回警示值以表明成功或失敗函式。

class Failure(Exception):passdef searcher():    if ...success...;        return ...founditem...    else:        raise Failure()try:    item=searcher()except Failure:    ...report...else:    ...use item here...

Python核心是動態類型和多態,通常更傾向於是用異常來發出這類情況的訊號,而不是警示性的傳回值。
3)在try外進行調試
空的except分句會捕捉任何程式執行時所引發的而未被捕捉到的異常。要取得發生的實際異常,可以從內建的
sys模組取出sys.exc_info函數的調用結果。這會返回一個元組,而元組之前兩個元素會自動包含當前異常的名稱,
以及相關的額外資料(如果有)。就基於類的異常而言,這兩個元素分別對應的是異常的類以及引發類的執行個體。
4)運行進程中的測試
5)關於sys.exc_info
sys.exc_info結果是獲得最近引發的異常更好的方式。如果沒有處理器正在處理,就返回包含了三個None值的元組。
否則,將會返回(type,value和traceback)
*type是正在處理的異常的異常類型(一個基於類的異常的類對象)
*value是異常參數(它的關聯值或raise的第二個參數,如果異常類型為類對象,就一定是類執行個體)
*traceback是一個traceback對象,代表異常最初發生時所調用的堆棧。
2、與異常有關的技巧
大致來說,Python的異常在使用上都很簡單。異常背後真正的技巧在於確定except分句要具體或多通用,以及try語句中要包括多少代碼。
1)應該封裝什嗎?
try語句中要包括多少代碼。
簡要原則
*經常會失敗的運算一般都應該封裝在try語句內。例如:和系統狀態銜接的運算(檔案開啟,通訊端調用等等)就是try的主要候選者。
*儘管這樣,上一條規則有寫特例:在簡單的指令碼中,你會希望這類運算失敗時終止程式,而不是被捕捉或被忽略。如果是一個重大的錯誤,
更應如此。Python的錯誤會產生有用的錯誤資訊,而且這通常就是所期望的最好的結果。
*應該try/finally中實現終止動作,從而保證它們的執行。這個語句形式可執行代碼,無論異常是否發生。
*偶爾,把對大型函數的調用封裝在單個try語句內,而不是讓函數本身零散著放入若干try的語句中。這樣會更方便。這樣的話,函數中的異常
就會往上傳遞到調用周圍的try,而你也可以減少函數中的代碼量。
2)捕捉太多:避免空except語句
如果使用空except語句,可能攔截到異常嵌套結構中較高層的try處理器所期待的事件這類代碼可能會捕捉無關的系統異常。如記憶體錯誤,一
程式錯誤,迭代停止以及系統推出等等,都會在Python中引發異常。這裡異常通常是不應該攔截的。
3)捕捉太少:使用基於類的分類
3、核心語言總結
1)Python工具集
一般而言,Python提供了一個有層次的工具集。
內建工具:
像字串,列表和字典這些內建類型,會讓編寫程式更為迅速。
Python擴充:
就更重要的任務來說,可以編寫自己的函數,模組以及類來擴充Python
已編譯的擴充:
Python的工具箱類型。
分類        例子
物件類型    列表,字典,檔案和字串
函數        len,range,apply,open
異常        IdexError,KeyError
模組        os,TKinter,pickle,re
屬性        __dict__,__name__,__class__
外部工具    NumPY,SWIG,Jython,IronPython
2)大型項目的開放工具
PyDoc
PyChecker 檢查
PyUnit  測試
doctest  測試
IDE 圖形介面
組態工具 profile是標準塊模組,為python實現原始碼組態工具。
調試器:原始碼調試器模組,稱為pdb, 類似C的命令列調試器gdb

>>> import pdb>>> pdb.run("main()") #下面就可以在互動模式下執行代碼調試命令> <string>(1)<module>()(Pdb) action1<function action1 at 0x28495bc4>(Pdb) action1()inner try

發布選擇:Python程式常見打包工具。py2exe,PyInstaller以及freeze都可以打包位元組碼以及Python虛擬機器
最佳化選項:Psyco系統提供了即時的編譯器,可以把Python位元組碼翻譯成二進位碼。
對大型醒目的提示
 

本文出自 “diege” 部落格,請務必保留此出處http://ipseek.blog.51cto.com/1041109/802914

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.