#! /usr/bin/env python
-*- coding:utf-8 -*-
'''
@Author:gcan
@Email:1528667112@qq.com
@Site:http://www.gcan.top
@File:exception.py
@Software:PyCharm
@Date:2018-05-14 22:48:40
@Version:1.0.0
'''
異常
Python使用被稱為異常 的特殊對(duì)象來管理程序執(zhí)行期間發(fā)生的錯(cuò)誤。每當(dāng)發(fā)生讓Python不知所措的錯(cuò)誤時(shí),它都會(huì)創(chuàng)建一個(gè)異常對(duì)象。如果你編寫了處理該異常的代碼猜憎,程序?qū)⒗^
續(xù)運(yùn)行;如果你未對(duì)異常進(jìn)行處理,程序?qū)⑼V股危@示一個(gè)traceback胰柑,其中包含有關(guān)異常的報(bào)告。
異常是使用try-except 代碼塊處理的爬泥。try-except 代碼塊讓Python執(zhí)行指定的操作柬讨,同時(shí)告訴Python發(fā)生異常時(shí)怎么辦。使用了try-except 代碼塊時(shí)袍啡,即便出現(xiàn)異常踩官,
程序也將繼續(xù)運(yùn)行:顯示你編寫的友好的錯(cuò)誤消息,而不是令用戶迷惑的traceback境输。
處理ZeroDivisionError 異常
下面來看一種導(dǎo)致Python引發(fā)異常的簡單錯(cuò)誤蔗牡。你可能知道不能將一個(gè)數(shù)字除以0,但我們還是讓Python這樣做吧: division.py
print(5/0)
顯然嗅剖,Python無法這樣做辩越,因此你將看到一個(gè)traceback:
Traceback (most recent call last):
File "/Users/mm/study_python/my_python/five/exception.py", line 23, in <module>
print(5/0)
ZeroDivisionError: division by zero
在上述traceback中,指出的錯(cuò)誤ZeroDivisionError 是一個(gè)異常對(duì)象信粮。Python無法按你的要求做時(shí)黔攒,就會(huì)創(chuàng)建這種對(duì)象。
在這種情況下强缘,Python將停止運(yùn)行程序督惰,并指出引發(fā)了哪種異常,而我們可根據(jù)這些信息對(duì)程序進(jìn)行修改旅掂。
下面我們將告訴Python姑丑,發(fā)生這種錯(cuò)誤時(shí)怎么辦;這樣,如果再次發(fā)生這樣的錯(cuò)誤辞友,我們就有備無患了。
使用try-except 代碼塊
當(dāng)你認(rèn)為可能發(fā)生了錯(cuò)誤時(shí),可編寫一個(gè)try-except 代碼塊來處理可能引發(fā)的異常称龙。你讓Python嘗試運(yùn)行一些代碼留拾,并告訴它如果這些代碼引發(fā)了指定的異常,該怎么辦鲫尊。 處理ZeroDivisionError 異常的try-except 代碼塊類似于下面這樣:
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
我們將導(dǎo)致錯(cuò)誤的代碼行print(5/0) 放在了一個(gè)try 代碼塊中痴柔。如果try 代碼塊中的代碼運(yùn)行起來沒有問題,Python將跳過except 代碼塊;
如果try 代碼塊中的代碼導(dǎo)致了 錯(cuò)誤疫向,Python將查找這樣的except 代碼塊咳蔚,并運(yùn)行其中的代碼,即其中指定的錯(cuò)誤與引發(fā)的錯(cuò)誤相同搔驼。
try 代碼塊中的代碼引發(fā)了ZeroDivisionError 異常谈火,因此Python指出了該如何解決問題的except 代碼塊,并運(yùn)行其中的代碼舌涨。
這樣糯耍,用戶看到的是一條友 好的錯(cuò)誤消息,而不是traceback:
使用異常避免崩潰
發(fā)生錯(cuò)誤時(shí)囊嘉,如果程序還有工作沒有完成温技,妥善地處理錯(cuò)誤就尤其重要。這種情況經(jīng)常會(huì)出現(xiàn)在要求用戶提供輸入的程序中;如果程序能夠妥善地處理無效輸入扭粱,就能再提示用 戶提供有效輸入舵鳞,而不至于崩潰。
下面來創(chuàng)建一個(gè)只執(zhí)行除法運(yùn)算的簡單計(jì)算器:
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
if second_number == 'q':
break
answer = int(first_number) / int(second_number)
print(answer)
這個(gè)程序提示用戶輸入一個(gè)數(shù)字琢蛤,并將其存儲(chǔ)到變量first_number 中;如果用戶輸入的不是表示退出的q蜓堕,就再提示用戶輸入一個(gè)數(shù)字,并將其存儲(chǔ)到變 量second_number 中虐块。
接下來俩滥,我們計(jì)算這兩個(gè)數(shù)字的商(即answer)。這個(gè)程序沒有采取任何處理錯(cuò)誤的措施贺奠,因此讓它執(zhí)行除數(shù)為0的除法運(yùn)算時(shí)霜旧,它將崩潰:
程序崩潰可不好,但讓用戶看到traceback也不是好主意儡率。不懂技術(shù)的用戶會(huì)被它們搞糊涂挂据,而且如果用戶懷有惡意,他會(huì)通過traceback獲悉你不希望他知道的信息儿普。
例如崎逃,他將知 道你的程序文件的名稱,還將看到部分不能正確運(yùn)行的代碼眉孩。有時(shí)候个绍,訓(xùn)練有素的攻擊者可根據(jù)這些信息判斷出可對(duì)你的代碼發(fā)起什么樣的攻擊勒葱。
通過將可能引發(fā)錯(cuò)誤的代碼放在try-except 代碼塊中,可提高這個(gè)程序抵御錯(cuò)誤的能力巴柿。錯(cuò)誤是執(zhí)行除法運(yùn)算的代碼行導(dǎo)致的凛虽,因此我們需要將它放到try-except 代碼塊
中。這個(gè)示例還包含一個(gè)else 代碼塊;依賴于try 代碼塊成功執(zhí)行的代碼都應(yīng)放到else 代碼塊中:
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
try:
answer = int(first_number) / int(second_number)
except ZeroDivisionError:
print("You can't divide by 0!")
else:
print(answer)
我們讓Python嘗試執(zhí)行try 代碼塊中的除法運(yùn)算广恢,這個(gè)代碼塊只包含可能導(dǎo)致錯(cuò)誤的代碼凯旋。依賴于try 代碼塊成功執(zhí)行的代碼都放在else 代碼塊中;在這個(gè)示例中,
如果除法運(yùn)算成功钉迷,我們就使用else 代碼塊來打印結(jié)果至非。
except 代碼塊告訴Python,出現(xiàn)ZeroDivisionError 異常時(shí)該怎么辦糠聪。如果try 代碼塊因除零錯(cuò)誤而失敗荒椭,我們就打印一條友好的消息,告訴用戶如何避免這種錯(cuò)誤枷颊。
程序?qū)⒗^續(xù)運(yùn)行戳杀,用戶根本看不到traceback:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 22
Second number: 0
You can't divide by 0!
First number: q
try-except-else 代碼塊的工作原理大致如下:Python嘗試執(zhí)行try 代碼塊中的代碼;只有可能引發(fā)異常的代碼才需要放在try 語句中。
有時(shí)候夭苗,有一些僅在try 代碼塊成功 執(zhí)行時(shí)才需要運(yùn)行的代碼;這些代碼應(yīng)放在else 代碼塊中信卡。except 代碼塊告訴Python,如果它嘗試運(yùn)行try 代碼塊中的代碼時(shí)引發(fā)了指定的異常题造,該怎么辦傍菇。
通過預(yù)測(cè)可能發(fā)生錯(cuò)誤的代碼,可編寫健壯的程序界赔,它們即便面臨無效數(shù)據(jù)或缺少資源丢习,也能繼續(xù)運(yùn)行,從而能夠抵御無意的用戶錯(cuò)誤和惡意的攻擊淮悼。
處理FileNotFoundError 異常 使用文件時(shí)咐低,一種常見的問題是找不到文件:你要查找的文件可能在其他地方、文件名可能不正確或者這個(gè)文件根本就不存在袜腥。對(duì)于所有這些情形见擦,都可使用try-except 代碼
塊以直觀的方式進(jìn)行處理。 我們來嘗試讀取一個(gè)不存在的文件羹令。下面的程序嘗試讀取文件alice.txt的內(nèi)容鲤屡,但我沒有將這個(gè)文件存儲(chǔ)在alice.py所在的目錄中
filename = 'alice.txt'
with open(filename) as f_obj:
contents = f_obj.read()
Traceback (most recent call last):
File "/Users/mm/study_python/my_python/five/exception.py", line 119, in <module>
with open(filename) as f_obj:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
在上述traceback中,最后一行報(bào)告了FileNotFoundError 異常福侈,這是Python找不到要打開的文件時(shí)創(chuàng)建的異常酒来。
在這個(gè)示例中,這個(gè)錯(cuò)誤是函數(shù)open() 導(dǎo)致的肪凛,因此要處理 這個(gè)錯(cuò)誤堰汉,必須將try 語句放在包含open() 的代碼行之前:
filename = 'alice.txt'
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFoundError:
msg = "Sorry, the file " + filename + " does not exist."
print(msg)
在這個(gè)示例中辽社,try 代碼塊引發(fā)FileNotFoundError 異常,因此Python找出與該錯(cuò)誤匹配的except 代碼塊衡奥,并運(yùn)行其中的代碼爹袁。最終的結(jié)果是顯示一條友好的錯(cuò)誤消息,而 不是traceback:
Sorry, the file alice.txt does not exist.
如果文件不存在矮固,這個(gè)程序什么都不做,因此錯(cuò)誤處理代碼的意義不大譬淳。下面來擴(kuò)展這個(gè)示例档址,看看在你使用多個(gè)文件時(shí),異常處理可提供什么樣的幫助邻梆。
決定報(bào)告哪些錯(cuò)誤
在什么情況下該向用戶報(bào)告錯(cuò)誤?在什么情況下又應(yīng)該在失敗時(shí)一聲不吭呢?如果用戶知道要分析哪些文件守伸,他們可能希望在有文件沒有分析時(shí)出現(xiàn)一條消息,將其中的原因告 訴他們浦妄。如果用戶只想看到結(jié)果尼摹,而并不知道要分析哪些文件,可能就無需在有些文件不存在時(shí)告知他們剂娄。向用戶顯示他不想看到的信息可能會(huì)降低程序的可用性蠢涝。Python的錯(cuò)誤 處理結(jié)構(gòu)讓你能夠細(xì)致地控制與用戶分享錯(cuò)誤信息的程度,要分享多少信息由你決定阅懦。
編寫得很好且經(jīng)過詳盡測(cè)試的代碼不容易出現(xiàn)內(nèi)部錯(cuò)誤和二,如語法或邏輯錯(cuò)誤,但只要程序依賴于外部因素耳胎,如用戶輸入惯吕、存在指定的文件、有網(wǎng)絡(luò)鏈接怕午,就有可能出現(xiàn)異常废登。憑 借經(jīng)驗(yàn)可判斷該在程序的什么地方包含異常處理塊,以及出現(xiàn)錯(cuò)誤時(shí)該向用戶提供多少相關(guān)的信息郁惜。