異常處理
什么是異常?
首先要清楚,什么是異常,異常就是程序運行時發生錯誤的信號(在程序出現錯誤時,則會產生一個異常,若程序沒有處理它,則會拋出該異常,程序的運行也隨之終止),在python中,錯誤觸發的異常如下
異常是由錯誤觸發的,那么錯誤有哪些情況呢?
1.語法錯誤:

#語法錯誤示范一 else #語法錯誤示范二 def test: pass #語法錯誤示范三 class Cal pass #語法錯誤示范四 print(hello 1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正)
2.邏輯錯誤

# res=1/0 # l=[1,2] # l[10] # age=input('>>: ') # age=int(age) # res=1/0 # l=[] # l[10000] # dic={} # dic['name'] # class Foo: # pass # Foo.x 2.邏輯錯誤示范
異常的種類
在平時編碼過程中,常見的異常有以下這些:
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x IOError 輸入/輸出異常;基本上是無法打開文件 ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典里不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量, 導致你以為正在訪問它 ValueError 傳入一個調用者不期望的值,即使值的類型是正確的
當然,還有其他異常,這里就不做過多演示。出現異常,我們肯定想到要處理,不然程序就直接報錯崩潰了。其實我們一直在處理異常,只是沒有發現,比如要判斷你輸入的內容是不是數字,我們以前是這么判斷的:
age = input('請輸入你的年齡: ').strip() if age.isdigit(): int(age) #這是主邏輯 elif age.isspace(): print('輸入的是空格!') elif len(age) == 0 : print('沒有輸入內容') else: print('其他異常!') >>>請輸入你的年齡: dasdasf >>>其他異常!
在這里if就是在處理異常,但是,如果我還有其他程序也要運行,那就要寫成這樣了:

age = input('請輸入你的年齡: ').strip() if age.isdigit(): int(age) #這是主邏輯 elif age.isspace(): print('輸入的是空格!') elif len(age) == 0 : print('沒有輸入內容') else: print('其他異常!') num = input('請輸入你的編號: ').strip() if num.isdigit(): int(num) #這是主邏輯 elif num.isspace(): print('輸入的是空格!') elif len(num) == 0 : print('沒有輸入內容') else: print('其他異常!')
這時候,你會發現,程序寫的很長,可讀性差,如果有十個這樣的輸入,那這個程序就沒法看了,這時候,python提供了一種異常處理的方法try...except...
part1 基本語法
try: 被執行的邏輯 except 異常名稱: 如果try中的邏輯出現異常,就執行這段邏輯
現在用這套方法來處理上面的異常,看看效果怎么樣:

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) except ValueError as e: #根據報錯知道錯誤類型是ValueError print(e) >>>請輸入你的年齡: 23 >>>請輸入你的編號: dwqd >>>invalid literal for int() with base 10: 'dwqd'
這樣一看代碼簡潔了很多啊,效果很完美
part2 異常只能用來處理指定的異常情況,其他情況不會處理
我們可以試一下,把錯誤類型改成別的,看看會怎樣:

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) except IndexError as e: print(e) >>>請輸入你的年齡: ffq Traceback (most recent call last): File "C:/Users/pengfy/PycharmProjects/untitled/錯誤與異常/錯誤與異常.py", line 26, in <module> int(age) ValueError: invalid literal for int() with base 10: 'ffq'
看來錯誤類型還要對應才行。
part3 多分支

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) l=[] l[10000] dic={} dic['name'] except ValueError as e: print(e) except IndexError as e: print(e) except KeyError as e: print(e) print('我繼續執行') >>>請輸入你的年齡: 12 >>>請輸入你的編號: 321 >>>list index out of range >>>我繼續執行
多加幾個except,就可以處理不同分支的異常了,這個和if...else...里面的elif很類似吧,現在就有疑問了,有沒有像if...else...里面else這樣的萬能處理呢,答案是肯定的。
part4 萬能異常
為了避免寫太多異常類型,或者一些不清楚的錯誤類型不知道怎么寫,那么可以用Exception:

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) dic={} dic['name'] l=[] l[10000] except Exception as e: print(e) print('我繼續執行') >>>請輸入你的年齡: 213 >>>請輸入你的編號: 23 >>>'name' >>>我繼續執行
這時候,有人就會覺得,萬能異常這么厲害,我還要寫什么其他異常的,全部用這個不就好啦?這個怎么說呢,要分兩點來看吧:
1.如果你想要的效果是,無論什么異常,你都直接無視或者說用一種處理機制,那么就直接用吧,沒問題,
2.如果你要根據異常類型處理不同機制,那還得用多分支的方式,
當然,你可以結合多分支和萬能異常一起使用啊,這樣多分支的健壯性會更好
part5 異常的其他結構
下面來看看異常處理的其他結構:

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) # l=[] # l[10000] # # dic={} # dic['name'] except ValueError as e: print('566') except IndexError as e: print('435') except KeyError as e: print('755') # except Exception as e: # print(e) else: print('try里面沒有異常出現,執行我') finally: print('不管有沒有異常,我都執行,我一般是做清理工作') print('我繼續執行') >>>請輸入你的年齡: 12 >>>請輸入你的編號: 21 >>>try里面沒有異常出現,執行我 >>>不管有沒有異常,我都執行,我一般是做清理工作 >>>我繼續執行

try: age = input('請輸入你的年齡: ').strip() int(age) num = input('請輸入你的編號: ').strip() int(num) l=[] l[10000] dic={} dic['name'] except ValueError as e: print('566') except IndexError as e: print('435') except KeyError as e: print('755') # except Exception as e: # print(e) else: print('try里面沒有異常出現,執行我') finally: print('不管有沒有異常,我都執行,我一般是做清理工作') print('我繼續執行') >>>請輸入你的年齡: 32 >>>請輸入你的編號: 13 >>>435 >>>不管有沒有異常,我都執行,我一般是做清理工作 >>>我繼續執行
看完兩個例子,可以知道這里面的else和if...else...里面的完全是兩回事,主要不要混淆。當try沒有異常時,else里面的邏輯才會執行,而finally不論在什么情況下都會執行,一般用來做清理工作,比如說你在try里面打開了一個問題,然后中途出現異常了,那么你的文件還在內存中,這時候你可以在finally里面關閉文件。
part6 主動觸發異常
我們學過主動觸發異常用的是raise,下面看一下能不能捕獲:

try: raise TypeError('打印錯誤') except TypeError as e: print(e) >>>打印錯誤
part7 自定義異常
如果你想自定義一個異常,也是可以的。異常是什么,就是一個類嘛,那我們就定義一個異常類看看:

class Pengfyexception(): def __init__(self,msg): self.msg = msg #報錯打印的內容 try: raise Pengfyexception('自定義的異常') except Pengfyexception as e: print(e) >>> Traceback (most recent call last): File "C:/Users/pengfy/PycharmProjects/untitled/錯誤與異常/錯誤與異常.py", line 103, in <module> raise Pengfyexception('自定義的異常') TypeError: exceptions must derive from BaseException
報錯了,看錯誤提示,再看看type錯誤是怎么寫的,原來要繼承一個叫BaseException的類,再試一下:

class Pengfyexception(BaseException): def __init__(self,msg): self.msg = msg #報錯打印的內容 try: raise Pengfyexception('自定義的異常') except Pengfyexception as e: print(e) >>>自定義的異常
完美了,成了。
part8 斷言
斷言可以說就是if的一種簡寫,直接看例子吧:

def test(): """一萬行代碼得到ret""" ret = 1 return ret res = test() assert res == 1 """繼續執行下面的代碼"""
如果判斷不正確:

def test(): """一萬行代碼得到ret""" ret = 1 return ret res = test() assert res == 2 """繼續執行下面的代碼""" >>>Traceback (most recent call last): File "C:/Users/pengfy/PycharmProjects/untitled/錯誤與異常/錯誤與異常.py", line 122, in <module> assert res == 2 AssertionError
這個完全可以用if寫:

def test(): """一萬行代碼得到ret""" ret = 1 return ret res = test() # assert res == 1 if res != 1: raise AssertionError # """繼續執行下面的代碼"""
效果完全一樣
part9 try...except...的好處和用法:
try...except...就是取代了if的那種方法,讓你的代碼在保證可讀性的情況下,還增強了健壯性,提高了容錯率,使用這種方法:
1.把錯誤處理和你的主邏輯分開了
2.代碼更容易組織,更清晰,復雜的任務更容易實現
3.更安全了,不會因為一些小錯誤導致程序崩潰
但是要清楚的一點是,if和try...except...都是python中處理異常的方法,不要學了try就說if和異常處理沒有關系了。其次,學完這個后發現try...except...很強大,是不是每一段代碼都可以加這個處理異常,就不用管報錯了,這是肯定不行的,try...except...還是要慎重使用,首先try...except是你附加給你的程序的一種異常處理的邏輯,與你的主要的工作是沒有關系的,這種東西加的多了,會導致你的代碼可讀性變差,然后異常處理本就不是你混亂邏輯的保姆,只有在錯誤發生的條件無法預知的情況下,才應該加上try...except