第一章 Python 入門
第二章 Python基本概念
第三章 序列
第四章 控制語句
第五章 函數
第六章 面向對象基礎
第七章 面向對象深入
第八章 異常機制
在本章, 我們首先會了解什麼是異常: 軟件程序在運行過程中,可能會遇到能使其不能正常運行的問題,我們稱之為異常,英文是: Exception
然後, 我們會了解捕獲異常的四種結構方式,
再然後, 我們梳理常見放的異常
再然後, 我們補充其他關於異常相關的問題,
最後, 我們通過使用 Pycharm 來體會異常的調試過程
程序在運行過程中發生的意外情況,稱為異常,
程序運行時一旦出現了異常,將會導致程序立即終止,異常之後的代碼將無法繼續執行,所以需要對異常進行處理
異常機制本質:
python中,引進了很多用來描述和處理異常的類,稱為異常類. 異常類定義中包含了該類異常的信息和對異常進行處理的方法.
下面較為完整的展示了python中內建異常類的繼承層次:
異常解決的關鍵:定位
當發生異常時,解釋器會報相關的錯誤信息,並會在控制台打印出相關錯誤信息.
我們只需按照從上到下的順序即可追溯(Trackback)錯誤發生的過程,最終定位引起錯誤的哪一行代碼.
實操代碼
# 測試簡單的0不能做除數異常
# 因為如果假設成立, 則說明 3/0=0 => 可以推導 0*0=3, 因為結果不成立, 因此假設不成立
# a = 3/0
def a():
print("run in a() start! ")
num = 1/0
print("run in a() end! ")
def b():
print("run in b() start! ")
a()
print("run in b() end! ")
def c():
print("run in c() start! ")
b()
print("run in c() end! ")
print("step1")
c()
print("step2")
結果輸出
從打印輸出結果來看, 最底層的方法(eg: a())出錯之後, 會在上層調用的方法位置處拋出異常.
因為錯誤信息以棧的形式輸出, 因此最頂層的調用因為先打印, 所以會先被我們看到. 因此對底層/最有用的信息一般位於最下面.
這種結構是最常見, 也是最常用的結構
語法結構
try:
被監控的可能引發異常的語句塊
except BaseException [as e]:
異常處理語句塊
注意事項:
實操代碼
def a():
print("run in a() start! ")
try:
num = 1 / 0
except BaseException as e:
print("捕獲異常之後執行此處代碼")
print("run in a() end! ")
def b():
print("run in b() start! ")
a()
print("run in b() end! ")
def c():
print("run in c() start! ")
b()
print("run in c() end! ")
print("step1")
c()
print("step2")
結果輸出
try…except 的結構可以捕獲所有的異常,工作中也很常見.
但是,一般建議盡量捕獲可能出現的多個異常(按照先子類後父類的順序),並且針對性寫出異常處理代碼
為了避免遺漏可能出現的異常,可以在最後增加 BaseException 。結構如下
語法結構
try:
被監控的、可能引發異常的語句塊
except Exception1:
處理Exception1的語句塊
except Exception2:
處理Exception2的語句塊
[...]
except BaseException:
處理可能遺漏的異常的語句塊
實操代碼
try:
a = input("請輸入被除數: ")
b = input("請輸入除數: ")
result = float(a)/float(b)
print(result)
except ZeroDivisionError:
print("異常: 0不能做除數")
except ValueError:
print("異常: 輸入的必須是數值類型!")
except BaseException as e:
print(e)
print(type(e))
結果輸出
在 try…except…else 結構的基礎上增加了 else 塊 .
如果 try 塊中沒有拋出異常,則執行else 塊. 如果 try 塊中拋出異常,則執行 except 塊,不執行 else 塊.
語法結構
try:
被監控的可能引發異常的語句塊
except BaseException [as e]:
異常處理語句塊
else:
沒有拋出異常時執行的語句塊
實操代碼
try:
a = input("請輸入被除數: ")
b = input("請輸入除數: ")
result = float(a)/float(b)
except BaseException as e:
print(e)
else:
print("兩數相除, 結果是:", result)
結果輸出
try…except…finally 結構中, finally 塊無論是否發生異常都會被執行, 通常用來釋放 try 塊中申請的資源
語法結構
try:
被監控的可能引發異常的語句塊
except BaseException [as e]:
異常處理語句塊
finally:
無論是否捕獲異常都會執行的語句塊
實操代碼
try:
a = input("請輸入被除數: ")
b = input("請輸入除數: ")
result = float(a)/float(b)
except BaseException as e:
print(e)
else:
print("兩數相除, 結果是:", result)
finally:
print("我是finally中的語句, 無論發生異常與否, 都執行!")
輸出結果
實操代碼2
try:
f = open("d:/a.txt",'r')
content = f.readline()
print(content)
except BaseException as e:
print(e)
finally:
f.close() #釋放資源. 此處也可能會發生異常。若發生異常,則程序終止,不會繼續往下執行
print("step4")
# Python中的異常都派生自 BaseException 類, 我們測試和列出常見的一些異常, 方便初學者掌握
# 1. SyntaxError :語法錯誤 | SyntaxError: invalid syntax. Perhaps you forgot a comma?
# int a =3
# 2. NameError :嘗試訪問一個沒有申明的變量 | NameError: name 'a' is not defined
# print(a)
# 3. ZeroDivisionError :除數為0錯誤(零除錯誤) | ZeroDivisionError: division by zero
# a = 3 / 0
# 4. ValueError :數值錯誤 | ValueError: could not convert string to float: 'skyII'
# float("skyII")
# 5. TypeError :類型錯誤 | TypeError: unsupported operand type(s) for +: 'int' and 'str'
# 123+"abc"
# 6. AttributeError :訪問對象的不存在的屬性 | AttributeError: 'int' object has no attribute 'sayhi'
# a = 100
# a.sayhi()
# 7. IndexError :索引越界異常 | IndexError: list index out of range
# a = [4, 5, 6]
# a[10]
# 8. KeyError :字典的關鍵字不存在 | KeyError: 'salary'
a = {
'name': "sari", 'age': 18}
a['salary']
由於 return 有兩種作用:結束方法運行、返回值.
我們一般不把 return放到異常處理結構中,而是放到方法最後.
實操代碼
一般不要將return語句放到try、except、else、finally塊中, 會發生一些意想不到的錯誤. 建議放到方法最後
如上面代碼, 這種寫法就會導致無論是否正確, 都會導致 try內的return無法執行而去執行 finally 內的語句
def testException():
try:
a = input("請輸入被除數: ")
b = input("請輸入除數: ")
result = float(a) / float(b)
return result
except BaseException as e:
print(e)
return "a"
finally:
print("我是finally中的語句, 無論發生異常與否, 都執行!")
return "b"
# return "b" # 正常情況應該放到這裡
print(testException())
結果輸出
finally 塊由於是否發生異常都會執行,通常我們作為放釋放資源的代碼.
其實,我們可以通過 with 上下文管理,更方便的實現釋放資源的操作.
with 上下文管理可以自動管理資源,在 with 代碼塊執行完畢後自動還原進入該代碼之前的現場或上下文.
不論何種原因跳出 with塊,不論是否有異常,總能保證資源正常釋放.
極大的簡化了工作,在文件操作、網絡通信相關的場合非常常用.
語法結構
with context_expr [ as var]:
語句塊
實操代碼
# 【示例】 with 上下文管理文件操作
with open("d:/dd.txt") as f:
for line in f:
print(line)
我們可以利用
traceback.print_exc(file=f)
, 將異常日志輸出到日志文件中
# traceback模塊和生成異常日志
import traceback
try:
print("step1")
num = 1 / 0
except:
# traceback.print_exc() # 直接輸出錯誤調用記錄
with open("d:/a.log", "a") as f: # 使用 traceback 將異常信息寫入日志文件
traceback.print_exc(file=f)
程序開發中,有時候我們也需要自己定義異常類.
自定義異常類一般都是運行時異常,通常繼承 Exception 或其子類即可.
命名一般以Error 、 Exception 為後綴. 自定義異常由 raise 語句主動拋出.
實操代碼
# 【示例】自定義異常類和raise語句
class AgeError(Exception):
def __init__(self, errorInfo):
Exception.__init__(self)
self.errorInfo = errorInfo
def __str__(self):
return str(self.errorInfo) + ", 年齡錯誤! 應該在 1-150之間!"
# 測試代碼
if __name__ == "__main__": # 如果為True, 則模塊是作為獨立文件運行, 可以執行測試代碼
age = int(input("輸入一個年齡: "))
if age < 1 or age > 150:
raise AgeError(age)
else:
print("正常的年齡: ", age)
結果輸出
程序運行到此處,暫時掛起,停止執行。我們可以詳細在此時觀察程序的運行情況,方便做出進一步的判斷
調試步驟
設置斷點:
在行號後面單擊即可增加斷點。在斷點上再單擊即可取消斷點
進入調試視圖
我們通過如下三種方式都可以進入調試視圖:單擊工具欄上的按鈕
/ 右鍵單擊編輯區,點擊: debug ‘模塊名’
/ 快捷鍵: shift+F9
進入調試視圖後,布局如下:
左側為“浏覽幀”:調試器列出斷點處,當前線程正在運行的方法,每個方法對應一個“棧幀”. 最上面是當前斷點所處的方法.
變量值觀察區: 調試器列出了斷點處所在方法相關的變量值. 我們可以通過它,查看變量的值的變化.
調試操作區
我們通過上圖中的按鈕進行調試操作,它們的含義如下: