1. 異常處理
異常就是在程序執行過程中發生的超出預期的事件。一般情況下,當程序無法正常執行時,都會拋出異常。
- 在開發過程中,由于疏忽或考慮不周,出現的設計錯誤。因此,在后期程序調試中應該根據錯誤信息,找到出錯位置,并對錯誤代碼進行分析和排錯。
- 難以避免的運行問題,如讀寫權限不一致、網絡異常、文件不存在、內存溢出、數據類型不一致等。針對這類異常,在設計時可以主動捕獲異常,防止程序意外終止。
- 主動拋出異常。在程序設計中,可以根據設計需要主動拋出異常,引導程序按照預先設計的邏輯運行,防止用戶不恰當地操作。
當發生異常時,一般應該捕獲異常,并妥善處理。如果異常未被處理,程序將終止運行,因此為程序添加異常處理,能使程序更健壯。
異常也是一種類型,所有的異常實例都繼承自BaseException
基類。使用Exception
可以捕獲所有類型的異常。
使用try…except
語句可以捕獲和處理異常,語句格式如下:
try: # 捕獲異常<執行語句>
except [異常類型[ as 別名]]: # 處理異常<處理語句>
把目標代碼放在try
語句中,在except
關鍵字后面設置可選的異常類型。如果省略異常類型,則表示捕獲全部異常類型。如果類型的名稱比較長,可以使用as關鍵字設置別名,方便在異常處理中引用。如果不需要處理異常,可以在except
代碼塊中使用pass
語句忽略。
try:'1' + 0 # 錯誤運算
except: # 捕獲所有的異常print('發生錯誤') # 提示錯誤信息try:f = open('test.txt','r') # 打開不存在的文件
except IOError as e: # 捕獲IOError類型異常print('錯誤編號:%s,錯誤信息:%s'%(e.errno,e.strerror)) #顯示錯誤信息try:f = open('test.txt','r') # 打開不存在的文件
except Exception as e: # 捕獲IOError類型異常print('錯誤編號:%s,錯誤信息:%s'%(e.errno,e.strerror)) #顯示錯誤信息
使用except語句可以同時處理多個異常,語法格式如下:
# 多個異常類型以元組形式進行設置,當try代碼發生其中一個,都被except處理。
try: # 捕獲異常<執行語句>
except ([Exception1[,Exception2,...,ExceptionN]]) [ as 別名]: # 處理異常<處理語句># 多個異常類型按優先順序分別進行處理
try: # 捕獲異常<執行語句>
except ([Exception1[,Exception2]]) [ as 別名]:<處理語句1>
...
except [Exception]:<處理語句N># 樣例
li = [] # 定義空列表
try:print(c) # 發生NameError異常print(3/0) # 發生ZeroDivisionError異常li[2] # 發生IndexError異常a = 123 + 'hello world' # 發生TypeError異常
except NameError as e:print('出現NameError異常!',e)
except ZeroDivisionError as e:print('出現ZeroDivisionError異常!',e)
except IndexError as e:print('出現IndexError異常!',e)
except TypeError as e:print('出現TypeError異常!',e)
except Exception as e:print('出現其它異常!',e)
在try
代碼塊中可以嵌套使用try…except
結構,設計多層嵌套的異常處理結構,異常對象可以從內向外逐層向上傳遞。
try:try:try:f = open('test.txt','r') # 打開并不存在的文件except NameError as e: # 捕獲未聲明的變量異常print('NameError')except IndexError as e: # 捕獲索引超出異常print('IndexError')
except IOError as e: # 獲取輸入或輸出的異常print('IOError')# 輸出:IOError
try…except
異常處理結構允許附帶一個可選的else
子句,用來設計當沒有發生異常時,正常處理的代碼塊。else
子句在異常發生時,不會被執行,只有當異常沒有發生時才會執行。
try: # 捕獲異常<執行語句>
except [異常類型][ as 別名]: # 處理異常<異常處理語句>
else: # 當異常未發生時執行<正常處理語句>try:f = open('test.txt','r') # 打開文件
except: # 如文件打開異常,則執行此print('出錯了')
else: # 如文件正常打開,則執行此print(f.read()) # 讀取文件內容try:f = open('test.txt','r') # 打開文件print(f.read()) # 如文件打開異常,則不執行此
except: # 如文件打開異常,則執行此print('出錯了')
善后處理:try…except
異常處理結構還可以帶一個可選的finally
子句,它表示無論異常是否發生,最后都要執行finally
語句塊。
try: # 捕獲異常<執行語句>
except [異常類型][ as 別名]: # 處理異常<異常處理語句>
else: # 當異常未發生時執行<正常處理語句>
finally: # 不管異常是否發生,最后都要執行<最后必須處理語句>import time
i = 1
while True: # 無限循環try:print(i) # 打印變量continue # 退出執行下一次循環print('永不執行') # 不執行該句finally: time.sleep(1) # 暫停1si += 1 # 遞增變量continue # 退出執行下一次循環print('永不執行') # 不執行語句
拋出異常:使用raise
語句主動拋出一個異常,這樣可以確保程序根據開發人員的設計邏輯執行,也可以對用戶的行為進行監控,避免意外操作。
raise [Exception[(args[,traceback])]]
raise 語句3種用法:
raise
:單獨一個raise。該語句將引發當前上下文捕獲的異常,默認引發RuntimeError
異常。raise 異常類名稱
:raise 后帶一個異常類名稱,表示引發執行指定類型的異常。raise 異常類名稱(描述信息)
:在引發指定類型的異常時,顯示異常的描述信息。
try:a = input('輸入一個數')if (not a.isdigit()):raise
except RuntimeError as e:print('引發異常:',repr(e)) # 引發異常: RuntimeError('No active exception to reraise')def test(num):try:if type(num) != int: # 如果為非數字的值,則拋出TypeError錯誤raise TypeError('參數不是數字') if num <= 0: # 如果為非正整數,則拋出ValueError錯誤raise ValueError('參數為不大于0的整數')print(num) # 打印數字except Exception as e:print(e) # 打印錯誤信息
test('1') # 參數不是數字
test(0) # 參數為不大于0的整數
test(2) # 2
自定義異常:自定義異常類型必須直接或間接繼承Exception
類。
class MyError(Exception):def __init__(self,msg):self.msg=msgdef __str__(self):return self.msg
try:raise MyError('自定義錯誤信息') # 主動拋出自定義錯誤
except MyError as e:print(e) # 打印:自定義錯誤信息
跟蹤異常:使用traceback
可以跟蹤異常,記錄異常發生時有關函數調用的堆棧信息。具體格式如下:
import traceback
try:1/0
except Exception as e:traceback.print_exc() # 打印詳細的錯誤信息traceback.format_exc() # 格式化字符串的形式返回錯誤信息# 把錯誤信息直接保存到外部文件中traceback.print_exc(file=open('log.log',mode='a',encoding='utf-8'))
2. 程序調試
在編寫程序中,常見錯誤有兩種:語法錯誤和異常。語法錯誤又稱解析錯誤,SyntaxError: invalid syntax 。
即使語句或表達式在語法上是正確的,但在執行時也可能會引發錯誤,該錯誤稱為異常,異常如果不被捕獲,被程序處理,則會中止程序,并顯示異常提示信息。
使用assert
語句可以定義斷言,斷言用于判斷一個表達式,在表達式條件為Flase的時候觸發異常,而不必等待程序運行后出現崩潰的情況。
def foo(s):n = int(s)assert n != 0,'n is zero!' # 設置斷言return 10/n
def main():foo('0')main()# 在交互式模式中,可以使用-O參數關閉 assert
python -O test1.py
使用pdb,pdb 是Python自帶的一個包,提供了一種交互的源代碼調試功能,主要特性包括:設置斷點、單步調試、進入函數調試、查看當前代碼、查看棧片段、動態改變變量的值等。pdb常用的調試命令:
- break或b:設置斷點。
- continue或c:繼續執行程序。
- list或l:查看當前行的代碼段。
- step或s:進入函數。
- return或r:執行代碼直到從當前函數返回。
- exit或q:中止并退出。
- next或n:執行下一行。
- p val:打印變量的值。
- help:幫助。
# 新建test1.py文件,代碼如下
s = '0'
n = int(s)
print(10/n)
# cd 命令進入到test1.py文件所在目錄
cd edition_master
# 啟動pdb調試器
python -m pdb test1.py
# 執行操作
l # 查看當前行的代碼段
n # 下一步
p s # 打印變量s
q # 退出
當程序代碼較多時,在可能出錯的地方插入pdb.set_trace()
,則不需要單步執行。