1. 程式人生 > >python-異常處理、模組化

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後的名稱直接繫結到匯入的模組物件,並將該名稱加入到本地名詞空間中。