python-異常處理、模塊化

異常處理

異常Exception


錯(cuò)誤 Error :錯(cuò)誤是可以避免的
邏輯錯(cuò)誤:算法寫(xiě)錯(cuò)了,加法寫(xiě)成了減法
筆誤:變量名寫(xiě)錯(cuò)了,語(yǔ)法錯(cuò)誤
函數(shù)或類(lèi)使用錯(cuò)誤,其實(shí)這也屬于邏輯錯(cuò)誤

異常 Exception :異常不可能避免
本意就是意外情況
這有個(gè)前提,沒(méi)有出現(xiàn)上面說(shuō)的錯(cuò)誤,也就是說(shuō)程序?qū)懙臎](méi)有問(wèn)題,但是在某些情況下,會(huì)出現(xiàn)一些意外,導(dǎo)致程序無(wú)法正常的執(zhí)行下去。
例如open函數(shù)操作一個(gè)文件,文件不存在,或者創(chuàng)建一個(gè)文件時(shí)已經(jīng)存在了,或者訪問(wèn)一個(gè)網(wǎng)絡(luò)文件,突然斷網(wǎng)了,這就是異常,是個(gè)意外的情況。

錯(cuò)誤和異常
在高級(jí)編程語(yǔ)言中,一般都有錯(cuò)誤和異常的概念,異常是可以捕獲,并被處理的,但是錯(cuò)誤是不能被捕獲的。

產(chǎn)生異常


產(chǎn)生:

  • raise 語(yǔ)句顯式的拋出異常
  • Python解釋器自己檢測(cè)到異常并引發(fā)它

程序會(huì)在異常拋出的地方中斷執(zhí)行,如果不捕獲,就會(huì)提前結(jié)束程序(其實(shí)是終止當(dāng)前線程的執(zhí)行)

raise語(yǔ)句
raise后什么都沒(méi)有,表示拋出最近一個(gè)被激活的異常,如果沒(méi)有,則拋類(lèi)型異常。這種方式很少用 。

raise后要求應(yīng)該是BaseException類(lèi)的子類(lèi)或?qū)嵗?,如果是?lèi),將被無(wú)參實(shí)例化。

異常類(lèi)及繼承層次


# 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及子類(lèi)


BaseException
所有內(nèi)建異常類(lèi)的基類(lèi)是BaseException

SystemExit
sys.exit(n)函數(shù)引發(fā)的異常,異常不捕獲處理,就直接交給Python解釋器,解釋器退出。n=0,正常退出,n=1異常退出。

如果except語(yǔ)句捕獲了該異常,則繼續(xù)向后面執(zhí)行,如果沒(méi)有捕獲住該異常SystemExit,解釋器直接退出程序。

import sys
 
print('before')
sys.exit(1)
print('SysExit')
print('outer') # 不執(zhí)行

# 捕獲這個(gè)異常
import sys
try:
    sys.exit(1)
except SystemExit: # 換成Exception
    print('SysExit')
print('outer') # 執(zhí)行

KeyboardInterrupt
對(duì)應(yīng)的捕獲用戶中斷行為Ctrl + C

try:
    import time
    while True:
        time.sleep(0.5)
        pass
except KeyboardInterrupt:
    print('ctl + c')
print('outer')

Exception及子類(lèi)

Exception是所有內(nèi)建的、非系統(tǒng)退出的異常的基類(lèi),自定義異常應(yīng)該繼承自它

SyntaxError 語(yǔ)法錯(cuò)誤
Python將這種錯(cuò)誤也歸到異常類(lèi)下面的Exception下的子類(lèi),但是這種錯(cuò)誤是不可捕獲的

ArithmeticError 所有算術(shù)計(jì)算引發(fā)的異常,其子類(lèi)有除零異常等

LookupError
使用映射的鍵或序列的索引無(wú)效時(shí)引發(fā)的異常的基類(lèi):IndexError, KeyError

自定義異常
從Exception繼承的類(lèi)

class MyException(Exception):
    pass

try:
    raise MyException()
except MyException: # 捕獲自定義異常
    print('catch the exception')

未實(shí)現(xiàn)和未實(shí)現(xiàn)異常

print(type(NotImplemented))
print(type(NotImplementedError))

#<class 'NotImplementedType'>
#<class 'type'>

NotImplemented是個(gè)值,單值,是NotImplementedType的實(shí)例

NotImplementedError是類(lèi)型,是異常,返回type

異常的捕獲


try:
    待捕獲異常的代碼塊
except [異常類(lèi)型]:
    異常的處理代碼塊

使用了try...except語(yǔ)句塊捕捉到了這個(gè)異常,異常生成位置之后語(yǔ)句將不再執(zhí)行,轉(zhuǎn)而執(zhí)行對(duì)應(yīng)的except部分的語(yǔ)句,最后執(zhí)行try...except語(yǔ)句塊之外的語(yǔ)句。

except 后接異常類(lèi)型,用來(lái)捕獲指定類(lèi)型的異常,except可以捕獲多個(gè)異常。

捕獲規(guī)則
捕獲是從上到下依次比較,如果匹配,則執(zhí)行匹配的except語(yǔ)句塊
如果被一個(gè)except語(yǔ)句捕獲,其他except語(yǔ)句就不會(huì)再次捕獲了
如果沒(méi)有任何一個(gè)except語(yǔ)句捕獲到這個(gè)異常,則該異常向外拋出

捕獲的原則
從小到大,從具體到寬泛

被拋出的異常,應(yīng)該是異常的實(shí)例,使用as子句接收這個(gè)拋出的異常。

finally子句


finally
最終,即最后一定要執(zhí)行的,try...finally語(yǔ)句塊中,不管是否發(fā)生了異常,都要執(zhí)行finally的部分

finally中一般放置資源的清理、釋放工作的語(yǔ)句,也可以在finally中再次捕捉異常。

異常的傳遞


def foo1():
?    return 1/0

def foo2():
?    print('foo2 start')
?    foo1()
?    print('foo2 stop')

foo2()

foo2調(diào)用了foo1,foo1產(chǎn)生的異常,傳遞到了foo2中。
異常總是向外層拋出,如果外層沒(méi)有處理這個(gè)異常,就會(huì)繼續(xù)向外拋出
如果內(nèi)層捕獲并處理了異常,外部就不能捕獲到了
如果到了最外層還是沒(méi)有被處理,就會(huì)中斷異常所在的線程的執(zhí)行。注意整個(gè)程序結(jié)束的狀態(tài)返回值。

# 線程中測(cè)試異常
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嵌套


內(nèi)部捕獲不到異常,會(huì)向外層傳遞異常
但是如果內(nèi)層有finally且其中有return、break語(yǔ)句,則異常就不會(huì)繼續(xù)向外拋出:異常被壓制。

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

異常的捕獲的時(shí)機(jī)

1.立即捕獲

需要立即返回一個(gè)明確的結(jié)果

def parse_int(s):
    try:
        return int(s)
    except:
        return 0
 
print(parse_int('s'))

2.邊界捕獲

封裝產(chǎn)生了邊界

例如,寫(xiě)了一個(gè)模塊,用戶調(diào)用這個(gè)模塊的時(shí)候捕獲異常,模塊內(nèi)部不需要捕獲、處理異常,一旦內(nèi)部處理了,外
部調(diào)用者就無(wú)法感知了。
例如,open函數(shù),出現(xiàn)的異常交給調(diào)用者處理,文件存在了,就不用再創(chuàng)建了,看是否修改還是刪除
例如,自己寫(xiě)了一個(gè)類(lèi),使用了open函數(shù),但是出現(xiàn)了異常不知道如何處理,就繼續(xù)向外層拋出,一般來(lái)說(shuō)最外
層也是邊界,必須處理這個(gè)異常了,否則線程退出

else子句

try:
    ret = 1 * 0
except ArithmeticError as e:
    print(e)
else:
    print('OK')
finally:
    print('fin')

else子句
沒(méi)有任何異常發(fā)生,則執(zhí)行

總結(jié)


try:
    <語(yǔ)句>    #運(yùn)行別的代碼
except <異常類(lèi)>:
    <語(yǔ)句>    # 捕獲某種類(lèi)型的異常
except <異常類(lèi)> as <變量名>:
    <語(yǔ)句>    # 捕獲某種類(lèi)型的異常并獲得對(duì)象
else:
    <語(yǔ)句>    #如果沒(méi)有異常發(fā)生
finally:
    <語(yǔ)句>    #退出try時(shí)總會(huì)執(zhí)行

try的工作原理

1、如果try中語(yǔ)句執(zhí)行時(shí)發(fā)生異常,搜索except子句,并執(zhí)行第一個(gè)匹配該異常的except子句
2、如果try中語(yǔ)句執(zhí)行時(shí)發(fā)生異常,卻沒(méi)有匹配的except子句,異常將被遞交到外層的try,如果外層不處理這個(gè)異
常,異常將繼續(xù)向外層傳遞。如果都不處理該異常,則會(huì)傳遞到最外層,如果還沒(méi)有處理,就終止異常所在的線程
3、如果在try執(zhí)行時(shí)沒(méi)有發(fā)生異常,將執(zhí)行else子句中的語(yǔ)句
4、無(wú)論try中是否發(fā)生異常,finally子句最終都會(huì)執(zhí)行。

模塊化

Python中只有一種模塊對(duì)象類(lèi)型,但是為了模塊化組織模塊的便利,提供了“包”的概念。模塊module,指的是Python的源代碼文件。

包package,指的是模塊組織在一起的和包名同名的目錄及其相關(guān)文件。

導(dǎo)入語(yǔ)句

語(yǔ)句 含義
import 模塊1,[模塊2....] 完全導(dǎo)入
import....as..... 模塊別名

import語(yǔ)句

1、找到指定的模塊,加載和初始化它,生成模塊對(duì)象。找不到,拋出ImportError

2、在import所在的作用域的局部命名空間中,增加名稱(chēng)和上一步創(chuàng)建的對(duì)象關(guān)聯(lián)。

總結(jié)

導(dǎo)入頂級(jí)模塊,其名稱(chēng)會(huì)加入到本地名詞空間中,并綁定到其模塊對(duì)象。

導(dǎo)入非頂層模塊,只將其頂級(jí)模塊名稱(chēng)加入到本地名稱(chēng)空間中。導(dǎo)入的模塊必須使用完全限定名稱(chēng)來(lái)訪問(wèn)。

如果使用了as,as后的名稱(chēng)直接綁定到導(dǎo)入的模塊對(duì)象,并將該名稱(chēng)加入到本地名詞空間中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容