0基礎學Python之二十五:異常處理(下)
來自專欄可樂編程【可樂編程】第28期 0基礎學Python之25:異常處理下_騰訊視頻
Hi 大家好,我是王可樂,歡迎回到我們的零基礎學 Python 課程。上一節課里,可樂為大家介紹了什麼是異常,以及如何識別、定位異常和尋找異常的解決辦法。在編寫代碼時,我們應該盡量避免出現異常。
不過,很多時候異常的發生也是不可避免的,特別是程序運行時需要讀取用戶輸入的時候。和很多其他語言一樣,Python 提供了一個異常的處理框架,可以用來對可能發生的異常進行預判和防護。這一節課里,可樂就來為大家介紹一下 Python 中的異常處理。
我們先來看一下,在第二課中為大家介紹的小遊戲代碼。
import randomnum = random.randint(1, 100) # 獲得一個隨機數is_done = False # 是否猜中的標記count = 0 # 玩家猜了幾次while not is_done: guess = int(input(請輸入一個[1, 100]的整數:)) if guess == num: is_done = True elif guess > num: print(大了,再猜猜!) elif guess < num: print(小了,再猜猜!) count += 1print(恭喜你,猜了{}次,終於猜對了!答案是{}!.format(count, num))
這段代碼的第 8 行中,我們調用了 Python 內置的 input() 函數,並且提示用戶在這裡輸入一個 1 到 100 之間的整數。用戶輸入之後,input() 函數返回用戶輸入的字元串,隨後我們用 int() 函數將用戶輸入的字元串轉換成一個整數。看起來沒有問題。不過,假如用戶沒有輸入整數,而是隨便輸入了一個其他字元串呢?我們把這段代碼拿出來,用 Python Shell 演示一下:
>>> guess = int(input(輸入一個整數:))輸入一個整數:abc # 我們在這裡輸入一個 abcTraceback (most recent call last): File "<stdin>", line 1, in <module>ValueError: invalid literal for int() with base 10: abc
可以看到,int() 函數拋出了一個叫做 ValueError 的異常,提示我們字元串 abc 不能被轉換成十進位的整數。由於我們沒有對異常做任何處理,在這個小遊戲里,如果用戶不小心輸入了非 0 到 9 之間的字元,那麼遊戲程序就會拋出這個異常而退出。
我們可以檢查用戶的輸入,判斷用戶是否輸入了合法的字元,在輸入合法的時候才將用戶輸入轉換為整數。例如,可以這樣來寫代碼:
>>> guess_str = input(輸入一個整數:)>>> if is_decimal(guess_str): # 判斷用戶輸入是否是十進位數字... guess = int(guess_str) # 輸入合法的情況下,我們將它轉換成整數類型... else: # 否則,我們提示用戶應該輸入一個整數... print(您的輸入不是整數: + guess_str)...
這裡,is_decimal() 需要我們自己實現,我們既可以用一個 for 循環檢查輸入字元串中的每一個字元,看看是否位於 ASCII 碼的數字範圍,也可以用以後將要學習到的正則表達式。
在日常寫代碼中,有很多類似的異常情況需要我們去考慮。特別是當我們面對用戶輸入的時候,如果我們盲目信任用戶的輸入,那麼不僅可能造成程序崩潰、結果錯誤,甚至,當我們的代碼在網路上運行時,還會引發許多安全問題,使得我們暴露在駭客的攻擊之下。
在編碼過程中對各種異常情況的防範處理,我們稱作防禦性編程。有很多資料為大家提供了防禦性編程的介紹、建議等,建議同學們搜索學習一下,為安全編程打下意識基礎。
上面的代碼里,我么使用了 IF 語句來對用戶輸入進行了額外的判斷。實際上,使用 Python 的異常處理功能,能夠更加簡單優雅地解決這個問題。我們還是先來看例子:
>>> try:... guess = int(input(輸入一個整數:))... except ValueError:... print(輸入錯誤,請您再試一次)...
這段代碼里,使用了 Python 的 try 和 except 關鍵字。這個語句的意思可以理解為:嘗試運行 try 關鍵字後面的語句,如果代碼運行沒有問題,那麼語句運行結束;如果代碼拋出了 ValueError 異常,那麼就執行 except 後面的語句。
在我們遊戲代碼的循環語句中,我們可以使用類似的代 碼包裹用戶輸入的語句,並且在 except 後面的語句塊中加一句 continue。這樣,在用戶輸入錯誤拋出異常時,我們就結束當前循環的運行,進入下一次循環,讓用戶重新輸入。大家可以自己嘗試改寫一下,或者拷貝可樂代碼倉庫中的代碼,試一下看是不是遊戲體驗好了很多呢?
上面這種代碼叫做異常的捕獲處理。使用異常捕獲,捕獲了異常之後,通常我們會提示用戶、記錄日誌,或者改變代碼執行流程,就像上面的 continue 語句一樣。和 IF 語句的 ELIF 子句類似,except 子句也可以重複多次;此外,和 IF 語句的 ELSE 子句類似,可以在 try 語句的最後使用 finally 關鍵字,Python 則會保證 finally 語句塊中的代碼總是會被運行;
try 語句自己也支持 else 子句,這裡的代碼則在沒有異常出現的時候總是會運行。我們來看下面的例子:
>>> try:... guess = int(input(輸入一個整數:)) # 仍然是提示用戶輸入整數... except ValueError:... print(輸入錯誤,請您再試一次) # 提示用戶輸入錯誤... except KeyboardInterrupt: # 然後,我們捕獲另一個異常,KeyboardInterrupt... print(輸入中斷) # 這個異常表示輸入中斷,我們提示給用戶... else: # 我們加入 else 子句... print(輸入正常) # else 子句總是在代碼沒有異常時運行,我們提示用戶... finally: # 最後,我們加入 finally 子句... print(這句話總是會執行)...
這段代碼里的 是一個特殊的異常,它會在用戶按 Ctrl-C 組合鍵的時候發生。
這裡,我們按下 Ctrl-C。可以看到,代碼提示我們"輸入中斷",這表示代碼成功捕獲了 KeyboardInterrupt 這個異常;此外,我們看到"這句話總是被執行"也列印了出來。
我們再來試一下,這一次我們輸入 abc,觸發 ValueError。可以看到,代碼提示我們輸入錯誤,並且這時也執行了 finally 子句。
最後,我們輸入一個正確的數字 42 試一下。大家可以看到,else 子句被執行了,這個子句只會在我們輸入正確,沒有觸發異常的時候運行。
此外,finally 子句也運行了,這就是 finally 關鍵字的作用,它在無論何種異常情況下都能執行。這個特性在做一些清理工作,例如關閉文件、緩存、資料庫連接等方面尤其有用。
我們上面這種寫法,用 except 語句捕獲並處理了不同的異常,實現了不同的異常情況區別對待的功能。
假如說有若干個異常情況可以做同樣的處理,except 也是可以同時捕獲多種不同類型異常的,寫法類似 except (XXXError, YYYError, ZZZError): ;
此外,except 後面也可以不寫任何異常類型,這時 except 就會嘗試捕獲所有發生的異常。不過,可樂不建議大家盲目地捕獲所有的異常,一方面是這樣我們可能掩蓋了代碼中的嚴重錯誤;另一方面,盲目捕獲所有異常也是一種偷懶的做法,總是這麼做,你就沒有動力研究各種可能發生的異常了。
最後,except 語句還支持用 as 關鍵字將捕獲的異常賦值給一個變數;捕獲的異常處理之後還可以使用 raise 語句拋出;並且,我們也能夠定義自己的異常類型。
好了,今天的課程可樂就先講到這裡。寫代碼難免遇到錯誤和異常,而在大型代碼中異常處理更是尤為重要。在使用內置函數、第三方庫時,大家可以多查閱文檔,看看別人寫的函數是否會拋出異常,又會拋出什麼樣的異常,需要做什麼樣的處理。希望大家以後在寫代碼的時候,都能認真思考,勤於調研,寫出不倒翁一樣穩定的代碼哦。
我們對異常處理的介紹先到這裡。下一節課將會是我們初級課程的最後一課,可樂會利用我們前面學習到的各種知識,帶領大家寫一個更加好玩,可訂製的小遊戲,敬請期待哦。
如果你喜歡我們的課程,歡迎關注我們的公眾號「可樂編程」,同時也請轉發給你的朋友們哦。可樂感謝大家的支持,我們下次課再見!
推薦閱讀:
※Python的1001種騷操作——基礎篇(1)
※Python實踐2-Paramiko實現互信主機間文件傳輸
※使用conda管理python環境
※用 Python 實現常用演算法和數據結構
※推薦一些相見恨晚的 Python 庫 「二」