使用 Python 編碼時(shí),即使是在語法和邏輯上正確的程序中,您也經(jīng)常會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。這些錯(cuò)誤可能是由無效輸入或某些前后矛盾引起的。在 很多語言中都提供了異常處理機(jī)制,在Python 中,您可以使用try和except塊來更優(yōu)雅地將大多數(shù)這些錯(cuò)誤作為異常處理。在本教程中,您將學(xué)習(xí)的一般語法try和except。然后我們將繼續(xù)編寫簡單的示例,討論可能出錯(cuò)的地方,并使用try和except塊提供糾正措施。
Python try 和 except 塊的語法
讓我們從了解Python 中try和except語句的語法開始介紹。通用模板如下圖所示:
try:
# There can be errors in this block
except <error type>:
# Do this to handle exception;
# executed if the try block throws an error
else:
# Do this if try block executes successfully without errors
finally:
# This block is always executed
讓我們看看不同塊的用途:
- 該try代碼塊是你想嘗試執(zhí)行的語句塊。但是,由于異??赡軙?huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤,所以此塊可能無法按預(yù)期工作。
- 該except代碼塊是觸發(fā)try塊時(shí)需要執(zhí)行的語句。它包含一組語句,這些語句通常會(huì)為您提供有關(guān)try塊內(nèi)出錯(cuò)的一些處理方法。
- 您應(yīng)該始終提及您打算在代碼塊內(nèi)作為異常捕獲的錯(cuò)誤類型,由上述代碼段中的占位符表示。例如:except<error type>
- 您也可以不在except指定<error type>. 但是,這不是推薦的做法,因?yàn)槟鷽]有考慮可能發(fā)生的不同類型的錯(cuò)誤。
在嘗試執(zhí)行try塊內(nèi)的代碼時(shí),也有可能發(fā)生多個(gè)錯(cuò)誤。
例如,您可能正在使用超出范圍的索引訪問列表,使用錯(cuò)誤的字典鍵,并嘗試打開一個(gè)不存在的文件 - 所有這些都應(yīng)該放在try塊內(nèi)。
在這種情況下,你可能會(huì)碰到?IndexError
?,?KeyError
?和?FileNotFoundError
?。并且您必須添加與except您預(yù)期的錯(cuò)誤數(shù)量一樣多的每種類型的錯(cuò)誤一個(gè)。
僅當(dāng)try塊在沒有錯(cuò)誤的情況下執(zhí)行時(shí),才會(huì)觸發(fā)else塊。當(dāng)您希望在try塊成功后執(zhí)行后續(xù)操作時(shí),這可能非常有用。例如,如果您嘗試成功打開一個(gè)文件,則可能需要讀取其內(nèi)容。
- 該finally塊總是被執(zhí)行,而不管其他塊中發(fā)生了什么。當(dāng)您想在執(zhí)行特定代碼塊后釋放資源時(shí),這很有用。
注意:else和finally塊是可選的。在大多數(shù)情況下,您可以只使用try塊來嘗試做某事,并將錯(cuò)誤捕獲為except塊內(nèi)的異常。
在接下來的幾分鐘內(nèi),您將使用迄今為止學(xué)到的知識(shí)來處理 Python 中的異常。讓我們開始吧。
如何處理Python中的ZeroDivision(除零異常)錯(cuò)誤
考慮下面所示的函數(shù)divide()。它接受兩個(gè)參數(shù) num和div ,并返回除法運(yùn)算的商num/div。
def divide(num,div):
return num/div
? 使用不同的參數(shù)調(diào)用函數(shù)會(huì)按預(yù)期返回結(jié)果:
res = divide(100,8)
print(res)
# Output
12.5
res = divide(568,64)
print(res)
# Output
8.875
此代碼工作正常,直到您嘗試除以零:
divide(27,0)
您會(huì)看到程序崩潰并拋出ZeroDivisionError:
# Output
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-19-932ea024ce43> in <module>()
----> 1 divide(27,0)
<ipython-input-1-c98670fd7a12> in divide(num, div)
1 def divide(num,div):
----> 2 return num/div
ZeroDivisionError: division by zero
您可以通過執(zhí)行以下操作將此除以零作為異常處理:
- 在try塊中,調(diào)用divide()函數(shù)。從本質(zhì)上講,您正在試圖將num除以div。
- 當(dāng)div為0的情況作為except塊內(nèi)的異常處理。
- 在此示例中,您可以通過打印一條消息來通知用戶他們嘗試除以零,從而排除?
ZeroDivisionError
?異常。
這顯示在下面的代碼片段中:
try:
res = divide(num,div)
print(res)
except ZeroDivisionError:
print("You tried to divide by zero :( ")
使用有效的輸入,代碼仍然可以正常工作。
divide(10,2)
# Output
5.0
當(dāng)您嘗試除零時(shí),您會(huì)收到發(fā)生異常的通知,并且程序會(huì)正常結(jié)束。
divide(10,0)
# Output
You tried to divide by zero :(
如何在 Python 中處理 TypeError(類型異常)
在本節(jié)中,您將看到如何在 Python 中使用try和except處理一個(gè) TypeError。
? 考慮以下函數(shù)add_10(),它接受一個(gè)數(shù)字作為參數(shù),將其加 10,然后返回加法的結(jié)果。
def add_10(num):
return num + 10
您可以使用任意數(shù)字參數(shù)調(diào)用該函數(shù),它會(huì)正常工作,如下所示:
result = add_10(89)
print(result)
#Output
99
現(xiàn)在嘗試add_10()使用"five"而不是調(diào)用5。
add_10("five")
您會(huì)注意到您的程序崩潰并顯示以下錯(cuò)誤消息:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-9844e949c84e> in <module>()
----> 1 add_10("five")
<ipython-input-13-2e506d74d919> in add_10(num)
1 def add_10(num):
----> 2 return num + 10
TypeError: can only concatenate str (not "int") to str
該錯(cuò)誤消息?TypeError: can only concatenate str (not "int") to str
?說明您只能連接兩個(gè)字符串,而不能向字符串添加整數(shù)。
現(xiàn)在,您有以下內(nèi)容:
- 給定一個(gè)數(shù)字my_num,嘗試使用my_num作為參數(shù)調(diào)用函數(shù)add_10()。如果參數(shù)是有效類型,則不存在異常
- 否則,將觸發(fā)與TypeError對(duì)應(yīng)的except塊,通知用戶參數(shù)的類型無效。
這一點(diǎn)解釋如下:
my_num = "five"
try:
result = add_10(my_num)
print(result)
except TypeError:
print("The argument `num` should be a number")
由于您現(xiàn)在已?將TypeError
?作為異常處理,因此您只會(huì)被告知參數(shù)的類型無效。
The argument `num` should be a number
如何在 Python 中處理 IndexError(無效索引)
如果您之前使用過 Python 列表或任何可迭代的 Python數(shù)據(jù)類型,您可能會(huì)遇到?IndexError
?.
這是因?yàn)橥ǔ:茈y跟蹤可迭代對(duì)象的所有更改。并且您可能正在嘗試訪問無效索引處的項(xiàng)。
? 在這個(gè)例子中,列表my_list有 4 個(gè)項(xiàng)。如果使用負(fù)索引,則有效索引為 0、1、2 和 3,以及 -1、-2、-3、-4。
由于2是有效的索引,您可以看到索引2中的項(xiàng)被打印出來,即C++:
my_list = ["Python","C","C++","JavaScript"]
print(my_list[2])
#Output
C++
如果您嘗試訪問位于有效索引范圍之外的索引處的項(xiàng),您將遇到?IndexError
?:
print(my_list[4])
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-7-437bc6501dea> in <module>()
1 my_list = ["Python","C","C++","JavaScript"]
----> 2 print(my_list[4])
IndexError: list index out of range
如果您熟悉該模式,您現(xiàn)在將使用try和except來處理索引錯(cuò)誤。
? 在下面的代碼片段中,您嘗試訪問由 指定的索引處的項(xiàng)目search_idx。
search_idx = 3
try:
print(my_list[search_idx])
except IndexError:
print("Sorry, the list index is out of range")
這里,search_idx( 3) 是一個(gè)有效的索引,并且打印出特定索引處的項(xiàng)目:
JavaScript
如果search_idx超出索引的有效范圍,則 except 塊將 捕獲IndexError為異常,并且不再有錯(cuò)誤消息。
search_idx = 4
try:
print(my_list[search_idx])
except IndexError:
print("Sorry, the list index is out of range")
而是顯示search_idx超出有效索引范圍的消息:
Sorry, the list index is out of range
如何在 Python 中處理 KeyError(鍵值對(duì)異常)
在使用 Python 詞典時(shí)可能遇到過KeyError。
? 考慮這個(gè)例子,你有一本字典my_dict。
my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
search_key = "non-existent key"
print(my_dict[search_key])
- 字典my_dict有 3 個(gè)鍵值對(duì),"key1:value1", "key2:value2", 和"key3:value3"
- 現(xiàn)在,您嘗試訪問字典并訪問與 key 對(duì)應(yīng)的值"non-existent key"。
正如預(yù)期的那樣,你會(huì)得到一個(gè)KeyError:
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-2-2a61d404be04> in <module>()
1 my_dict ={"key1":"value1","key2":"value2","key3":"value3"}
2 search_key = "non-existent key"
----> 3 my_dict[search_key]
KeyError: 'non-existent key'
幾乎可以像處理IndexError一樣處理KeyError.
- 您可以嘗試訪問與指定的鍵對(duì)應(yīng)的值search_key。
- 如果search_key確實(shí)是一個(gè)有效的鍵,則打印出相應(yīng)的值。
- 如果由于密鑰不存在而遇到異常,您可以使用該except塊讓用戶知道。
這在下面的代碼片段中進(jìn)行了解釋:
try:
print(my_dict[search_key])
except KeyError:
print("Sorry, that's not a valid key!")
Sorry, that's not a valid key!
? 如果您想提供其他操作,例如無效密鑰的名稱,您也可以這樣做:密鑰可能拼寫錯(cuò)誤,使其無效。在這種情況下,讓用戶知道使用的密鑰可能會(huì)幫助他們修復(fù)拼寫錯(cuò)誤。
您可以通過捕獲無效密鑰<error_msg>并在發(fā)生異常時(shí)打印的消息中使用它來實(shí)現(xiàn)此目的:
try:
print(my_dict[search_key])
except KeyError as error_msg:
print(f"Sorry,{error_msg} is not a valid key!")
? 注意鍵名也是如何打印出來的:
Sorry,'non-existent key' is not a valid key!
如何在 Python 中處理 FileNotFoundError(文件找不到異常)
在 Python 中處理文件時(shí)發(fā)生的另一個(gè)常見錯(cuò)誤是FileNotFoundError.
? 在以下示例中,您嘗試通過指定open()函數(shù)的路徑來打開文件my_file.txt。并且您想讀取該文件并打印出該文件的內(nèi)容。
但是,您尚未在指定位置創(chuàng)建文件。
如果您嘗試運(yùn)行下面的代碼片段,您將得到FileNotFoundError:
my_file = open("/content/sample_data/my_file.txt")
contents = my_file.read()
print(contents)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-4-4873cac1b11a> in <module>()
----> 1 my_file = open("my_file.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'my_file.txt'
并使用try和 except,您可以執(zhí)行以下操作:
- 嘗試在try塊中打開文件。
- 處理FileNotFoundError:通過except讓用戶知道他們?cè)噲D打開一個(gè)不存在的文件塊。
- 如果try塊成功,并且文件確實(shí)存在,則讀取并打印出文件的內(nèi)容。
- 在finally塊中,關(guān)閉文件,以免浪費(fèi)資源?;叵胍幌挛募⑷绾侮P(guān)閉,而不管文件打開和讀取步驟中發(fā)生了什么。
try:
my_file = open("/content/sample_data/my_file.txt")
except FileNotFoundError:
print(f"Sorry, the file does not exist")
else:
contents = my_file.read()
print(contents)
finally:
my_file.close()
請(qǐng)注意您是如何將錯(cuò)誤作為異常處理的,程序結(jié)束時(shí)會(huì)優(yōu)雅地顯示以下消息:
Sorry, the file does not exist
? 讓我們考慮else觸發(fā)塊的情況。該文件my_file.txt現(xiàn)在位于前面提到的路徑中。
這是文件my_file.txt包含的內(nèi)容:
現(xiàn)在,重新運(yùn)行之前的代碼片段按預(yù)期工作。
這一次,文件my_file.txt存在,else塊被觸發(fā)并打印出其內(nèi)容,如下所示:
我希望這能闡明您在處理文件時(shí)如何處理異常。