Python 入門教程/異常處理
如果你以前沒見過它們,那你還不夠努力。它們是什麼?錯誤。異常。問題。明白我的意思嗎?我的這個程式就遇到了這種問題
- 程式碼示例 1 - 有錯誤的程式
def menu(list, question):
for entry in list:
print 1 + list.index(entry),
print ") " + entry
return raw_input(question) - 1
# running the function
# remember what the backslash does
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')
print 'You picked answer ' + (answer + 1)
這只是我們之前做的選單程式的一個例子。在我看來,它看起來很完美。至少在我第一次嘗試執行它之前是這樣的。執行程式,會發生什麼?
你程式碼中最常見的問題都是你自己造成的。很遺憾,但事實如此。當我們嘗試執行我們殘疾的程式時,我們看到了什麼?
- 程式碼示例 2 - 錯誤資訊
Traceback (most recent call last):
File "/home/steven/errortest.py", line 10, in -toplevel-
answer = menu(< I'll snip it here >)
File "/home/steven/errortest.py", line 6, in menu
return raw_input(question) - 1
TypeError: unsupported operand type(s) for -: 'str' and 'int'
這是什麼意思?Python 試圖告訴你(但很難找到一個合適的詞)你不能將一個字母字串和一個數字連線成一個文字字串。讓我們看一下錯誤資訊,看看它如何告訴我們這一點。
File "/home/steven/errortest.py", line 10, in -toplevel-告訴我們一些資訊。File "/home/steven/errortest.py" 告訴我們錯誤發生在哪個檔案中。如果你使用了很多相互引用的模組,這將很有用。line 10, in -toplevel- 告訴我們錯誤發生在檔案中的第 10 行,並且發生在頂層(也就是說,沒有縮排)。answer = menu(['A','B','C','D','E','F','H','I'],'Which letter is your favourite? ')重複了錯誤發生的程式碼。- 由於這行程式碼呼叫了一個函式,接下來的兩行描述了錯誤發生在函式中的哪個位置。
TypeError: unsupported operand type(s) for -: 'str' and 'int'告訴你錯誤型別。在本例中,它是一個 "TypeError",表示你嘗試對不相容的變數進行減法運算。
對於一個錯誤,會有多個檔案和程式碼列表,因為錯誤是由於兩行程式碼的互動而發生的(例如,當使用函式時,錯誤發生在呼叫函式的那一行,以及函式中出錯的那一行)。
現在我們知道了問題是什麼,如何解決它?錯誤資訊已經隔離了問題所在,所以我們只關注那段程式碼。
- 程式碼示例 3 - 呼叫 menu 函式
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')
這是一個函式呼叫。錯誤發生在函式中的以下一行程式碼中
- 程式碼示例 4 - 錯誤發生的地方
return raw_input(question) - 1
raw_input 始終返回一個字串,因此出現了我們的問題。讓我們將其更改為 input(),當你輸入一個數字時,它會返回一個數字。
- 程式碼示例 5 - 修復錯誤
return input(question) - 1
錯誤修復!
好的,當你在正常情況下執行程式時,它能正常工作。但是,如果你嘗試一些奇怪的事情呢?輸入一個字母(比如 "m")而不是一個數字?哎喲!
- 程式碼示例 6 - 另一個錯誤資訊
Traceback (most recent call last):
File "/home/steven/errortest.py", line 10, in -toplevel-
answer = menu(< I'll snip it here >)
File "/home/steven/errortest.py", line 6, in menu
return input(question) - 1
File "", line 0, in -toplevel-
NameError: name 'm' is not defined
這告訴我們什麼?有兩行程式碼列表 - 一行在第 10 行,另一行在第 6 行。它告訴我們,當我們在第 10 行呼叫 menu 函式時,在第 6 行(我們減去 1 的地方)發生了錯誤。如果你知道 input() 函式的作用,這就會說得通 - 我閱讀了一些資料並進行了測試,發現如果你輸入一個字母或單詞,它會認為你在提及一個變數!所以,在第 6 行,我們試圖從變數 "m" 中減去 1,而 "m" 不存在。
你不知道如何修復它?最好的也是最簡單的方法之一是使用 try 和 except 運算子。
這是一個在程式中使用 try 的示例。
- 程式碼示例 7 - try 運算子
try:
function(world, parameters)
except:
print world.errormsg
這是一個我試圖修復的非常混亂的程式碼段的示例。首先,執行 try: 下面的程式碼。如果發生錯誤,編譯器會跳到 except 部分並列印 world.errormsg。程式不會停止並崩潰,它會執行 except: 下面的程式碼,然後繼續執行。
讓我們在程式碼中發生錯誤的地方(第 6 行)試試看。現在 menu 函式是
- 程式碼示例 8 - 測試我們的修復
def menu(list, question):
for entry in list:
print 1 + list.index(entry),
print ") " + entry
try:
return input(question) - 1
except NameError:
print "Enter a correct number"
當你被要求輸入一個數字時,嘗試輸入一個字母,看看會發生什麼。糟糕。我們修復了一個問題,但現在它導致了另一個問題。這種情況經常發生。(有時你會陷入迴圈,因為你的程式碼太亂了)。讓我們看看這個錯誤。
- 程式碼示例 9 - 又一個錯誤資訊
Traceback (most recent call last):
File "/home/steven/errortest.py", line 12, in -toplevel-
print 'You picked answer', (answer + 1)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
這次發生了什麼?menu 函式沒有返回值 - 它只打印了一個錯誤資訊。當我們在程式結束時嘗試列印返回值加 1 時,返回值是什麼?沒有返回值?那麼 1 加...,嗯,我們不知道我們要把 1 加到什麼東西上!
我們只需返回任何數字,但這相當於撒謊。我們真正應該做的是重寫程式,讓它能夠處理這個異常。用什麼處理?用 try 和 except!
- 程式碼示例 10 - 另一個解決方案
# from when we finish defining the function
answer = menu(['A','B','C','D','E','F','H','I'],\
'Which letter is your favourite? ')
try:
print 'You picked answer', (answer + 1)
# you can put stuff after a comma in the 'print' statement,
# and it will continue as if you had typed in 'print' again
except:
print '\nincorrect answer.'
# the '\n' is for formatting reasons. Try without it and see.
問題再次解決。
我們上面使用的這種方法並不推薦。為什麼?因為除了我們知道可能發生的錯誤外,except: 還捕獲了所有其他錯誤。如果這意味著我們永遠看不到可能導致後續問題發生的錯誤怎麼辦?如果 except: 捕獲了所有可能的錯誤,我們就無法控制要處理哪些錯誤,以及我們想要看到的其他錯誤,因為到目前為止我們還沒有處理它們。我們也幾乎無法在一個程式碼塊中處理多種型別的錯誤。當一切都無望時,該怎麼辦?以下是一個遇到這種情況的程式碼示例。
- 程式碼示例 11 - 我們面臨的問題
print 'Subtraction program, v0.0.1 (beta)'
a = input('Enter a number to subtract from > ')
b = input('Enter the number to subtract > ')
print a - b
好的,你輸入了兩個數字,它能正常工作。輸入一個字母,它會給你一個 NameError。讓我們重寫程式碼,只處理 NameError。我們將把程式放在一個迴圈中,這樣如果發生錯誤,它就會重新啟動(使用 continue,它會從頂部重新開始迴圈,使用 break,它會退出迴圈)。
- 程式碼示例 12 - 處理 NameError
print 'Subtraction program, v0.0.2 (beta)'
loop = 1
while loop == 1:
try:
a = input('Enter a number to subtract from > ')
b = input('Enter the number to subtract > ')
except NameError:
print "\nYou cannot subtract a letter"
continue
print a - b
try:
loop = input('Press 1 to try again > ')
except NameError:
loop = 0
在這裡,如果輸入錯誤,我們將重新啟動迴圈。在第 12 行,我們假設你想要退出程式,如果你沒有按 1,所以我們退出了程式。
但是仍然存在問題。如果我們留空或輸入一個不尋常的字元,例如 ! 或 ;,程式會給我們一個 SyntaxError。讓我們處理這個問題。當我們要求輸入要減去的數字時,我們會給出不同的錯誤資訊。當我們要求按 1 時,我們還會假設使用者想要退出。
- 程式碼示例 13 - 現在,處理 SyntaxError
print 'Subtraction program, v0.0.3 (beta)'
loop = 1
while loop == 1:
try:
a = input('Enter a number to subtract from > ')
b = input('Enter the number to subtract > ')
except NameError:
print "\nYou cannot subtract a letter"
continue
except SyntaxError:
print "\nPlease enter a number only."
continue
print a - b
try:
loop = input('Press 1 to try again > ')
except (NameError,SyntaxError):
loop = 0
如你所見,你可以使用多個 except,每個 except 處理一個不同的問題。你也可以使用一個 except 來處理多個異常,方法是將它們放在括號內並用逗號隔開。
現在我們有一個程式,它很難被終端使用者崩潰。作為最後的挑戰,看看你是否能使其崩潰。我已經想到了一種方法 - 如果你仔細閱讀了關於人為錯誤的部分,你可能知道它是什麼。