python-異常處理、模組化
異常處理
異常Exception
錯誤 Error :錯誤是可以避免的
邏輯錯誤:演算法寫錯了,加法寫成了減法
筆誤:變數名寫錯了,語法錯誤
函式或類使用錯誤,其實這也屬於邏輯錯誤
異常 Exception :異常不可能避免
本意就是意外情況
這有個前提,沒有出現上面說的錯誤,也就是說程式寫的沒有問題,但是在某些情況下,會出現一些意外,導致程式無法正常的執行下去。
例如open函式操作一個檔案,檔案不存在,或者建立一個檔案時已經存在了,或者訪問一個網路檔案,突然斷網了,這就是異常,是個意外的情況。
錯誤和異常
在高階程式語言中,一般都有錯誤和異常的概念,異常是可以捕獲,並被處理的,但是錯誤是不能被捕獲的。
產生異常
產生:
- raise 語句顯式的丟擲異常
- Python直譯器自己檢測到異常並引發它
程式會在異常丟擲的地方中斷執行,如果不捕獲,就會提前結束程式(其實是終止當前執行緒的執行)
raise語句
raise後什麼都沒有,表示丟擲最近一個被啟用的異常,如果沒有,則拋型別異常。這種方式很少用 。
raise後要求應該是BaseException類的子類或例項,如果是類,將被無參例項化。
異常類及繼承層次
# Python異常的繼承 BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- RuntimeError | +-- RecursionError +-- MemoryError +-- NameError +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- LookupError | +-- IndexError | +-- KeyError +-- SyntaxError +-- OSError | +-- BlockingIOError | +-- ChildProcessError | +-- ConnectionError | | +-- BrokenPipeError | | +-- ConnectionAbortedError | | +-- ConnectionRefusedError | | +-- ConnectionResetError | +-- FileExistsError | +-- FileNotFoundError | +-- InterruptedError | +-- IsADirectoryError | +-- NotADirectoryError | +-- PermissionError | +-- ProcessLookupError | +-- TimeoutError
BaseException及子類
BaseException
所有內建異常類的基類是BaseException
SystemExit
sys.exit(n)函式引發的異常,異常不捕獲處理,就直接交給Python直譯器,直譯器退出。n=0,正常退出,n=1異常退出。
如果except語句捕獲了該異常,則繼續向後面執行,如果沒有捕獲住該異常SystemExit,直譯器直接退出程式。
import sys print('before') sys.exit(1) print('SysExit') print('outer') # 不執行 # 捕獲這個異常 import sys try: sys.exit(1) except SystemExit: # 換成Exception print('SysExit') print('outer') # 執行
KeyboardInterrupt
對應的捕獲使用者中斷行為Ctrl + C
try:
import time
while True:
time.sleep(0.5)
pass
except KeyboardInterrupt:
print('ctl + c')
print('outer')
Exception及子類
Exception是所有內建的、非系統退出的異常的基類,自定義異常應該繼承自它
SyntaxError 語法錯誤
Python將這種錯誤也歸到異常類下面的Exception下的子類,但是這種錯誤是不可捕獲的
ArithmeticError 所有算術計算引發的異常,其子類有除零異常等
LookupError
使用對映的鍵或序列的索引無效時引發的異常的基類:IndexError, KeyError
自定義異常
從Exception繼承的類
class MyException(Exception):
pass
try:
raise MyException()
except MyException: # 捕獲自定義異常
print('catch the exception')
未實現和未實現異常
print(type(NotImplemented))
print(type(NotImplementedError))
#<class 'NotImplementedType'>
#<class 'type'>
NotImplemented是個值,單值,是NotImplementedType的例項
NotImplementedError是型別,是異常,返回type
異常的捕獲
try:
待捕獲異常的程式碼塊
except [異常型別]:
異常的處理程式碼塊
使用了try...except語句塊捕捉到了這個異常,異常生成位置之後語句將不再執行,轉而執行對應的except部分的語句,最後執行try...except語句塊之外的語句。
except 後接異常型別,用來捕獲指定型別的異常,except可以捕獲多個異常。
捕獲規則
捕獲是從上到下依次比較,如果匹配,則執行匹配的except語句塊
如果被一個except語句捕獲,其他except語句就不會再次捕獲了
如果沒有任何一個except語句捕獲到這個異常,則該異常向外丟擲
捕獲的原則
從小到大,從具體到寬泛
被丟擲的異常,應該是異常的例項,使用as子句接收這個丟擲的異常。
finally子句
finally
最終,即最後一定要執行的,try...finally語句塊中,不管是否發生了異常,都要執行finally的部分
finally中一般放置資源的清理、釋放工作的語句,也可以在finally中再次捕捉異常。
異常的傳遞
def foo1():
return 1/0
def foo2():
print('foo2 start')
foo1()
print('foo2 stop')
foo2()
foo2呼叫了foo1,foo1產生的異常,傳遞到了foo2中。
異常總是向外層丟擲,如果外層沒有處理這個異常,就會繼續向外丟擲
如果內層捕獲並處理了異常,外部就不能捕獲到了
如果到了最外層還是沒有被處理,就會中斷異常所在的執行緒的執行。注意整個程式結束的狀態返回值。
# 執行緒中測試異常
import threading
import time
def foo1():
return 1/0
def foo2():
time.sleep(3) # 3秒後丟擲異常
print('foo2 start')
foo1()
print('foo2 stop')
t = threading.Thread(target=foo2)
t.start()
while True:
time.sleep(1)
print('Everything OK')
if t.is_alive():
print('alive')
else:
print('dead')
try巢狀
內部捕獲不到異常,會向外層傳遞異常
但是如果內層有finally且其中有return、break語句,則異常就不會繼續向外丟擲:異常被壓制。
try:
try:
ret = 1 / 0
except KeyError as e:
print(e)
finally:
print('inner fin')
except:
print('outer catch')
finally:
print('outer fin')
#輸出
#inner fin
#outer catch
#outer fin
異常的捕獲的時機
1.立即捕獲
需要立即返回一個明確的結果
def parse_int(s):
try:
return int(s)
except:
return 0
print(parse_int('s'))
2.邊界捕獲
封裝產生了邊界
例如,寫了一個模組,使用者呼叫這個模組的時候捕獲異常,模組內部不需要捕獲、處理異常,一旦內部處理了,外
部呼叫者就無法感知了。
例如,open函式,出現的異常交給呼叫者處理,檔案存在了,就不用再建立了,看是否修改還是刪除
例如,自己寫了一個類,使用了open函式,但是出現了異常不知道如何處理,就繼續向外層丟擲,一般來說最外
層也是邊界,必須處理這個異常了,否則執行緒退出
else子句
try:
ret = 1 * 0
except ArithmeticError as e:
print(e)
else:
print('OK')
finally:
print('fin')
else子句
沒有任何異常發生,則執行
總結
try:
<語句> #執行別的程式碼
except <異常類>:
<語句> # 捕獲某種型別的異常
except <異常類> as <變數名>:
<語句> # 捕獲某種型別的異常並獲得物件
else:
<語句> #如果沒有異常發生
finally:
<語句> #退出try時總會執行
try的工作原理
1、如果try中語句執行時發生異常,搜尋except子句,並執行第一個匹配該異常的except子句
2、如果try中語句執行時發生異常,卻沒有匹配的except子句,異常將被遞交到外層的try,如果外層不處理這個異
常,異常將繼續向外層傳遞。如果都不處理該異常,則會傳遞到最外層,如果還沒有處理,就終止異常所在的執行緒
3、如果在try執行時沒有發生異常,將執行else子句中的語句
4、無論try中是否發生異常,finally子句最終都會執行。
模組化
Python中只有一種模組物件型別,但是為了模組化組織模組的便利,提供了“包”的概念。模組module,指的是Python的原始碼檔案。
包package,指的是模組組織在一起的和包名同名的目錄及其相關檔案。
匯入語句
語句 | 含義 |
---|---|
import 模組1,[模組2....] | 完全匯入 |
import....as..... | 模組別名 |
import語句
1、找到指定的模組,載入和初始化它,生成模組物件。找不到,丟擲ImportError
2、在import所在的作用域的區域性名稱空間中,增加名稱和上一步建立的物件關聯。
總結
匯入頂級模組,其名稱會加入到本地名詞空間中,並繫結到其模組物件。
匯入非頂層模組,只將其頂級模組名稱加入到本地名稱空間中。匯入的模組必須使用完全限定名稱來訪問。
如果使用了as,as後的名稱直接繫結到匯入的模組物件,並將該名稱加入到本地名詞空間中。