我們在編程時常常要和各種錯誤信息打交道,當(dāng)Python解釋器發(fā)現(xiàn)程序的錯誤時端蛆,就會拋出“異常”(Exception)來提示錯誤——這種情況可能發(fā)生于“編譯時”和“運行時”這兩個不同的階段:Python程序在運行之前要先編譯嫌拣,如果編譯未通過就不會開始運行——你可以在IPython一次交互中輸入包含多條語句的程序來驗證一下(按Ctrl+Enter換行异逐,按Shift+Enter提交):
In [1]: print(2/3)
...: print(2///3)
...: print("結(jié)束")
File "<ipython-input-1-90ecc1ce7c0b>", line 2
print(2///3)
^
SyntaxError: invalid syntax
In [2]: print(2/3)
...: print(2/0)
...: print("結(jié)束")
...:
0.6666666666666666
Traceback (most recent call last):
File "<ipython-input-2-7d58b37c849b>", line 2, in <module>
print(2/0)
ZeroDivisionError: division by zero
上面第一段程序的第二句不符合語法,編譯因此中斷并拋出“語法錯誤”(SyntaxError)異常腥例,這就屬于編譯時錯誤酝润;通過編譯的程序在運行期間仍可能出現(xiàn)導(dǎo)致程序中止的問題——上面第二段程序在語法上沒有問題,第一句也正常執(zhí)行了构回,但第二句中除數(shù)為零的運算違反數(shù)學(xué)規(guī)則疏咐,運行因此中止并拋出“除零錯誤”(ZeroDivisionError)異常,這就屬于運行時錯誤茁肠。
運行時錯誤難免會發(fā)生——用戶輸入的數(shù)據(jù)不完整缩举、打開的文件格式不正確或連接的網(wǎng)絡(luò)不通暢等等仅孩,都可能導(dǎo)致拋出異常。開發(fā)者應(yīng)該預(yù)先考慮到各種異常情況京腥,增加相應(yīng)的代碼來處理運行時錯誤以避免程序意外中止溅蛉。所有異常對象都是特定異常類的實例,最基本的異常類是BaseException欠气,通常在編程中需要處理的異常則都繼承自BaseException的子類Exception镜撩。如果想要查看異常類的繼承關(guān)系,可以使用mro方法返回“方法解析順序”(Method Resolution Order)——這實際上就是類的繼承順序宜鸯,一直上溯到object類為止。此外鸿市,你還可以使用raise語句直接“召喚”異常适贸,或使用assert語句“指明”條件來觸發(fā)異常。
In [3]: SyntaxError.mro()
Out[3]: [SyntaxError, Exception, BaseException, object]
In [4]: ZeroDivisionError.mro()
Out[4]: [ZeroDivisionError, ArithmeticError, Exception, BaseException, object]
In [5]: help(Exception.mro)
Help on built-in function mro:
mro(...) method of builtins.type instance
mro() -> list
return a type's method resolution order
In [6]: raise Exception("發(fā)生了錯誤")
Traceback (most recent call last):
File "<ipython-input-6-2b67d8d306dd>", line 1, in <module>
raise Exception("發(fā)生了錯誤")
Exception: 發(fā)生了錯誤
In [7]: a = 20
...: assert a <= 10, "數(shù)值過大"
...:
Traceback (most recent call last):
File "<ipython-input-7-58c21e2f5947>", line 2, in <module>
assert a <= 10, "數(shù)值過大"
AssertionError: 數(shù)值過大
Python提供try語句來實現(xiàn)異常處理:“嘗試”執(zhí)行可能出錯的代碼冯遂,如有“異常”就進行相應(yīng)處理——提醒用戶再次輸入蛤肌、檢查文件或重新連網(wǎng)等等裸准,使程序能夠順暢地運行下去。下面我們來看一個非常簡單的計算程序:根據(jù)輸入的算式輸出答案——使用eval函數(shù)能把字符串作為表達式來求值盐肃,但是用戶可能輸入不合法的表達式权悟,導(dǎo)致運行時錯誤的發(fā)生,因此就要使用try語句來處理異常:嘗試運行try代碼段谦铃,如無異常則運行之后的語句榔昔,如有異常就轉(zhuǎn)而執(zhí)行except代碼段,這樣即使用戶輸入錯誤的內(nèi)容嘹朗,程序也不至于崩潰茧彤。
"""calc.py 簡單的計算程序
"""
ans = ""
while True:
ask = input("輸入算式或回車退出:")
if ask == "":
break
try:
ans = eval(ask)
except:
pass
print(ans)
以上程序中的pass語句表示什么也不做直接放過,這當(dāng)然不好——正如Python之禪所言“錯誤不可放過”——以下程序捕獲拋出的Exception類(包括其所有繼承者)實例并賦值給變量e壁顶,這樣就能用repr(e)返回異常類型及提示信息溜歪,通知我們具體發(fā)生了什么問題再繼續(xù)運行:
except Exception as e:
ans = repr(e)
你可以在try語句中使用多個except子句,捕獲不同類型的異常進行分別處理调衰,例如輸出自定義的提示信息自阱。此外沛豌,你還可以添加一個finally子句,在其中編寫無論是否發(fā)生異常都要“最終”執(zhí)行的代碼加派。
except SyntaxError:
ans = "語法不正確"
except ZeroDivisionError:
ans = "除數(shù)不能為零"
except Exception as e:
ans = "發(fā)生{}錯誤".format(e.__class__.__name__)
finally:
print("輸出結(jié)果:", end="")
下面讓我們來看一個在線隨機圖片API的測試程序:訪問指定的“歲月小筑”網(wǎng)址會返回一張隨機圖片的網(wǎng)址芍锦,然后調(diào)用默認瀏覽器打開:
"""urlgetpic.py “歲月小筑”隨機圖片API測試程序
獲取一張隨機圖片的網(wǎng)址并用瀏覽器打開
"""
from urllib.request import urlopen
import webbrowser
url = "http://img.xjh.me/random_img.php?return=url"
def main():
try:
pic = urlopen(url).read().decode()
webbrowser.open("http:" + pic)
except Exception as e:
print(repr(e))
if __name__ == "__main__":
main()
網(wǎng)絡(luò)總會有連不上的時候娄琉,所以訪問在線資源的代碼應(yīng)該用try語句加以保護。
——編程原來這樣……
編程小提示:開放API
API指“應(yīng)用編程接口”(Application Programming Interface)檬输,其作用是讓外部開發(fā)者可以調(diào)用程序的特定功能匈棘,而又無需關(guān)心程序內(nèi)部的細節(jié)。在互聯(lián)網(wǎng)時代逃默,把網(wǎng)絡(luò)服務(wù)封裝成一系列數(shù)據(jù)接口開放出去供第三方開發(fā)者使用簇搅,這就叫做開放API——從更廣泛的意義上講瘩将,任何網(wǎng)站都是API凹耙,你可以編寫程序從網(wǎng)站中獲取所需要的信息,就像調(diào)用本地函數(shù)返回結(jié)果一樣肠仪。