2020年9月15日星期二

09 零基礎入門學習Python 第9章 異常處理

申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!

9章 異常處理
9.1 你不可能總是對的
因為我們是人,不是神,所以我們經常會犯錯。當然程式師也不例外,就算是經驗豐富的碼農,也不能保證寫出來的代碼百分之百沒有任何問題(要不哪來那麼多0Day漏洞)。另外,作為一個合格的程式師,在程式設計的時候一定要意識到一點,就是永遠不要相信你的用戶。要把他們想像成熊孩子,把他們想像成駭客,這樣你寫出來的程式自然會更加安全和穩定。
那麼既然程式總會出問題,我們就應該學會用適當的方法去解決問題。程式出現邏輯錯誤或者使用者輸入不合法都會引發異常,但這些異常並不是致命的,不會導致程式崩潰死掉。可以利用Python提供的異常處理機制,在異常出現的時候及時捕獲,並從內部自我消化掉。
那麼什麼是異常呢?舉個例子:
這裡當然假設用戶的輸入是正確的,但只要用戶輸入一個不存在的檔案名,那麼上面的代碼就不堪一擊:
上面的例子就拋出了一個FileNotFoundError異常,那Python通常還可能拋出哪些異常呢?這裡給大家做個總結,今後遇到這樣的異常時就不會感覺到陌生了。
1AssertionError:斷言語句(assert)失敗
大家還記得斷言語句吧?在關於分支和迴圈的章節裡講過。當assert這個關鍵字後邊的條件為假的時候,程式將停止並拋出AssertionError異常。assert語句一般是在測試程式的時候用於在代碼中置入檢查點:
2AttributeError:嘗試訪問未知的物件屬性
當試圖訪問的物件屬性不存在時拋出的異常:
3IndexError:索引超出序列的範圍
在使用序列的時候就常常會遇到IndexError異常,原因是索引超出序列範圍的內容:
4KeyError:字典中查找一個不存在的關鍵字
當試圖在字典中查找一個不存在的關鍵字時就會引發KeyError異常,因此建議使用dict.get()方法:
5NameError:嘗試訪問一個不存在的變數
當嘗試訪問一個不存在的變數時,Python會拋出NameError異常:
6OSError:作業系統產生的異常
OSError顧名思義就是作業系統產生的異常,像打開一個不存在的檔會引發FileNotFoundError,而這個FileNotFoundError就是OSError的子類。例子上面已經演示過,這裡就不再重複。
7SyntaxErrorPython的語法錯誤
如果遇到SyntaxErrorPython的語法錯誤,這時Python的代碼並不能繼續執行,你應該先找到並改正錯誤:
8TypeError:不同類型間的無效操作
有些類型不同是不能相互進行計算的,否則會拋出TypeError異常:
9ZeroDivisionError:除數為零
地球人都知道除數不能為零,所以除以零就會引發ZeroDivisionError異常:
好了,知道程式拋出異常就說明這個程式有問題,但問題並不致命,所以可以通過捕獲這些異常,並糾正這些錯誤就行。那應該如何捕獲和處理異常呢?
異常捕獲可以使用try語句來實現,任何出現在try語句範圍內的異常都會被及時捕獲到。try語句有兩種實現形式:一種是try-except,另一種是try-finally

9.2 try-except語句
try-except語句格式如下:
try: 檢測範圍 except Exception[as reason]: 出現異常(Exception)後的處理代碼
try-except語句用於檢測和處理異常,舉個例子來說明這一切是如何工作的:
# p9_2.py f = open('我為什麼是一個文檔.txt') print(f.read()) f.close()
以上代碼在我為什麼是一個文檔.txt”這個文檔不存在的時候,Python就會報錯說文件不存在:
顯然這樣的用戶體驗不好,因此可以這麼修改:
# p9_3.py try: f = open('我為什麼是一個文檔.txt)' print(f.read()) f.close() except OSError: print('檔打開的過程中出錯啦T_T)'
上面的例子由於使用了大家習慣的語言來表述錯誤資訊,使用者體驗當然會好很多:
但是從程式師的角度來看,導致OSError異常的原因有很多(例如FileExistsErrorFileNotFoundErrorPermissionError等等),所以可能會更在意錯誤的具體內容,這裡可以使用as把具體的錯誤資訊給列印出來:
except OSError as reason: print('檔出錯啦T_T\n錯誤原因是:' + str(reason))

9.2.1 針對不同異常設置多個except
一個try語句還可以和多個except語句搭配,分別對感興趣的異常進行檢測處理:
#p9_4.py try: sum = 1 + '1' f = open('我是一個不存在的文檔.txt)' print(f.read()) f.close() except OSError as reason: print('檔出錯啦T_T\n錯誤原因是:' + str(reason)) except TypeError as reason: print('類型出錯啦T_T\n錯誤原因是:' + str(reason))

9.2.2 對多個異常統一處理
except後邊還可以跟多個異常,然後對這些異常進行統一的處理:
#p9_5.py try: int('abc') sum + 1 + '1' f = open('我是一個不存在的文檔.txt)' print(f.read()) f.close() except (OSError, TypeError): print('出錯啦T_T\n錯誤原因是:' + str(reason))

9.2.3 捕獲所有異常
如果你無法確定要對哪一類異常進行處理,只是希望在try語句塊裡一旦出現任何異常,可以給用戶一個看得懂的提醒,那麼可以這麼做:
… except: print('出錯啦~)' …
不過通常不建議你這麼做,因為它會隱藏所有程式師未想到並且未做好處理準備的錯誤,例如當使用者輸入Ctrl+C試圖終止程式,卻被解釋為KeyboardInterrupt異常。另外要注意的是,try語句檢測範圍內一旦出現異常,剩下的語句將不會被執行。

9.3 try-finally語句
如果我是一個不存在的文檔確實存在,open()函數正常返回檔物件,但異常卻發生在成功打開檔後的sum=1+'1'語句上。此時Python將直接跳到except語句,也就是說,檔打開了,但並沒有執行關閉檔的命令:
#p9_6.py try: f = open('我是一個不存在的文檔.txt)' print(f.read()) sum = 1 + '1' f.close() except: print('出錯啦')
為了實現像這種就算出現異常,但也不得不執行的收尾工作(比如在程式崩潰前保存使用者文檔),引入了finally來擴展try
#p9_7.py try: f = open('我是一個不存在的文檔.txt)' print(f.read()) sum = 1 + '1' except: print('出錯啦') finally: f.close()
如果try語句塊中沒有出現任何執行階段錯誤,會跳過except語句塊執行finally語句塊的內容。如果出現異常,則會先執行except語句塊的內容再執行finally語句塊的內容。總之,finally語句塊中的內容就是確保無論如何都將被執行的內容。

9.4 raise語句
有讀者可能會問,我的代碼能不能自己拋出一個異常呢?答案是可以的,你可以使用raise語句拋出一個異常:
拋出的異常還可以帶參數,表示異常的解釋:

9.5 豐富的else語句
有讀者可能會說,else語句還有啥好講的?經常跟if語句進行搭配用於條件判斷嘛。沒錯,對於大多數程式設計語言來說,else語句都只能跟if語句搭配。但在Python裡,else語句的功能更加豐富。
Python中,else語句不僅能跟if語句搭,構成要麼怎樣,要麼不怎樣的句式;它還能跟迴圈語句(for語句或者while語句),構成幹完了能怎樣,幹不完就別想怎樣的句式;其實else語句還能夠跟剛剛講的異常處理進行搭配,構成沒有問題?那就幹吧的句式,下邊逐個給大家解釋。
1.要麼怎樣,要麼不怎樣
典型的if-else搭配:
if條件: 條件為真執行 else: 條件為假執行
2.幹完了能怎樣,幹不完就別想怎樣
else可以跟forwhile迴圈語句配合使用,但else語句塊只在迴圈完成後執行,也就是說,如果迴圈中間使用break語句跳出迴圈,那麼else裡邊的內容就不會被執行了。舉個例子:
這個小程式主要是求使用者輸入的數的最大約數,如果是素數的話就順便提醒這是一個素數。注意要使用地板除法(count=num//2)哦,否則結果會出錯。使用暴力的方法一個個嘗試(num%count==0),如果符合條件則列印出最大的約數,並break,同時不會執行else語句塊的內容了。但如果一直沒有遇到合適的條件,則會執行else語句塊內容。
for語句的用法跟while一樣,這裡就不重複舉例了。
3.沒有問題?那就幹吧
else語句還能跟剛剛學的異常處理進行搭配,實現跟與迴圈語句搭配差不多:只要try語句塊裡沒有出現任何異常,那麼就會執行else語句塊裡的內容啦。舉個例子:
#p9_9.py try: int('abc') except ValueError as reason: print('出錯啦:' + str(reason)) else: print('沒有任何異常!)'

9.6 簡潔的with語句
有讀者可能覺著打開檔又要關閉檔,還要關注異常處理有點煩人,所以Python提供了一個with語句,利用這個語句抽象出檔操作中頻繁使用的try/except/finally相關的細節。對檔操作使用with語句,將大大減少代碼量,而且你再也不用擔心出現檔打開了忘記關閉的問題了(with會自動幫你關閉文件)。舉個例子:
#p9_10.py try: f = open('data.txt', 'w)' for each_line in f: print(each_line) except OSError as reason: print('出錯啦:' + str(reason)) finally: f.close()
使用with語句,可以改成這樣:
#p9_11.py try: with open('data.txt', 'w')as f: for each_line in f: print(each_line) except OSError as reason: print('出錯啦:' + str(reason))
是不是很方便呢?有了with語句,就再也不用擔心忘記關閉檔了。


0 留言:

發佈留言