Python語法速查: 10. 異常

返回目錄html

 

本篇索引python

(1)內置異常緩存

(2)自定義異常網絡

(3)主動引起異常app

(4)捕捉異常異步

(5)error模塊函數

(6)with語句oop

 

 

 (1)內置異常

異常的基類:ui

如下這些異常做爲具體異常的基類,都不會被顯式引起,可是可使用它們捕捉某種錯誤。編碼

基類名稱 說明
BaseException

全部內置異常的基類,其餘全部內置異常都派生自該類。

  Exception 全部內置的非系統退出異常都派生自此類(即除了:SystemExit, GeneratorExit, KeyboardInterrupt以外的全部內置異常)。全部用戶自定義異常也應當派生自此類。
  ArithmeticError 算術運算異常的基類,包括溢出、除零等等。
  LookupError 索引和鍵錯誤的基類。
  BufferError 當與緩衝區相關的操做沒法執行時引起,通常由用戶自行繼承使用。

 

具體異常

如下異常屬於常常被引起的異常。

具體異常名稱 說明
如下異常直接繼承自:BaseException
GeneratorExit 由生成器的.close()方法引起。
KeyboardInterrupt 由鍵盤中斷(一般爲 Ctrl-C)生成。
SystemExit 程序退出,通常系統函數sys.exit()引起。
如下異常繼承自:BaseException -> Exception
StopIteration 引起後可中止迭代。
StopAsyncIteration 由異步迭代器引起中止迭代。
AssertionError 當 assert 語句失敗時引起。
AttributeError 當屬性引用或屬性賦值失敗時引起。
EOFError 當 input() 函數未讀取任何數據即達到文件結束條件 (EOF) 時將被引起。(文件操做的諸如read()和readline()方法等I/O操做在遇到EOF時會返回空字符串,而不是引起異常)
ImportError 當 import 語句沒法找到模塊或者 from 沒法在模塊中找到名稱時引起。Python3.3版本後,對此異常添加了name和path屬性,用來表示「嘗試導入的模塊名稱」和「觸發異常的文件所在的路徑」。
    ModuleNotFoundError ImportError的子類,Python3.6版本新加入。當一個模塊沒法找到時由import引起。
MemoryError 可用內存不足(但仍可挽救時)時將被引起。
NameError 在局部或全局命名空間中未找到某個名稱時引起。
    UnboundLocalError NameError的子類。引用了未綁定的局部變量時引起。
OSError Python3.3起,如下都是OSError的別名:IOError、EnvironmentError、WindowsError(僅限Windows中)。
操做系統錯誤,主要由os模塊中的函數引發。
    BlockingIOError OSError的子類。當一個操做阻塞一個設置爲非阻塞操做的對象時引起。
    ChildProcessError OSError的子類。當對子進程進行操做失敗時將引起,對應於Linux系統調用時的errno中的 ECHILD
    ConnectionError 與鏈接相關問題的基類。
        BrokenPipeError ConnectionError的子類。當試圖寫入另外一端已被關閉的管道,或是試圖寫入已關閉寫入的套接字時引起。對應於Linux系統調用時的errno中的 EPIPE 和 ESHUTDOWN。
        ConnectionAbortedError ConnectionError的子類。當鏈接嘗試被對端停止時將被引起。對應於Linux系統調用時的errno中的 ECONNABORTED。
        ConnectionRefusedError ConnectionError的子類。當鏈接嘗試被對端拒絕時將被引起。對應於Linux系統調用時的errno中的 ECONNREFUSED。
        ConnectionResetError ConnectionError的子類。當鏈接被對端重置時將被引起。對應於Linux系統調用時的errno中的 ECONNRESET。
    FileExistsError OSError的子類。當試圖建立一個已存在的文件或目錄時將被引起。對應於Linux系統調用時的errno中的 EEXIST。
    FileNotFoundError OSError的子類。當所請求的文件或目錄不存在時引起。對應於Linux系統調用時的errno中的 ENOENT。
    InterruptedError OSError的子類。當系統調用被輸入信號中斷時引起。對應於Linux系統調用時的errno中的 EINTR。
    IsADirectoryError OSError的子類。當請求對一個目錄執行文件操做時引起。對應於Linux系統調用時的errno中的 EISDIR。
    NotADirectoryError OSError的子類。當請求對一個非目錄對象執行目錄操做時引起。對應於Linux系統調用時的errno中的 ENOTDIR。
    PermissionError OSError的子類。當沒有對相應文件操做權限的時候引起。對應於Linux系統調用時的errno中的 EACCESS和 EPERM
    ProcessLookupError OSError的子類。當給定的進程不存在時引起。對應於Linux系統調用時的errno中的 ESRCH。
    TimeoutError OSError的子類。當一個系統函數發生系統級超時的狀況下將被引起。對應於Linux系統調用時的errno中的 ETIMEDOUT。
ReferenceError 在弱引用訪問某個已被垃圾回收的屬性時引起,關於弱引用可參見werkref模塊。
RuntimeError 當檢測到一個不屬於任何其餘類別的錯誤時引起。
    NotImplementedError RuntimeError的子類。當基類的抽象方法須要派生類實現,而派生類未實現時引起。
    RecursionError RuntimeError的子類。Python解釋器檢測發現超過最大遞歸深度時引起。
SyntaxError 解析器語法錯誤。
    IndentationError SyntaxError的子類。縮進錯誤時引起。
        TabError IndentationError的子類。當縮進包含對製表符和空格符不一致的使用時引起。
SystemError Python解釋器中的內部錯誤。
TypeError 當一個操做或函數被應用於類型不適當的對象時引起。
ValueError 當操做或函數接收到具備正確類型但值不適合的參數時引起。
    UnicodeError ValueError的子類。Unicode編碼或解碼錯誤時引起。
        UnicodeEncodeError UnicodeError的子類。Unicode編碼錯誤。
        UnicodeDecodeError UnicodeError的子類。Unicode解碼錯誤。
        UnicodeTranslateError UnicodeError的子類。在轉換過程當中產生的與Unicode相關的錯誤。
如下異常繼承自:BaseException -> Exception -> ArithmeticError
FloatingPointError 目前未被使用。
OverflowError 當一個運算結果大到沒法表示時將被引起。
ZeroDivisionError 當除法或取餘運算的第二個參數爲零時將被引起。
如下異常繼承自:BaseException -> Exception -> LookupError
IndexError 序列的下標超出範圍時引起。
KeyError 映射(字典)中未找到鍵時引起。
如下警告繼承自:BaseException -> Exception -> Warning
    DeprecationWarning 與已棄用特性相關警告的基類。
    PendingDeprecationWarning 對於已過期並預計在將來棄用,但目前還沒有棄用的特性相關警告的基類。
    RuntimeWarning 與模糊的運行時行爲相關的警告的基類。
    SyntaxWarning 與模糊的語法相關的警告的基類。
    UserWarning 用戶代碼所產生警告的基類。
    FutureWarning 與已棄用特性相關警告的基類。
    ImportWarning 與在模塊導入中可能的錯誤相關的警告的基類。
    UnicodeWarning 與 Unicode 相關的警告的基類。
    BytesWarning 與 bytes 和 bytearray 相關的警告的基類。
    ResourceWarning 與資源使用相關的警告的基類。 會被默認的警告過濾器忽略。

 

 

 

  (2)自定義異常

能夠經過繼承Exceptions類而定義本身的異常。定義完以後,可使用raise語句引起這個新的異常,以下例所示:

 

class NetworkError(Exception):
    pass
    
raise NetworkError('Cannot find host')

下例顯示瞭如何在自定義異常中帶有多個異常值:

class NetworkError(Exception):
    def __init__(self, errno, msg):
        self.args = (errno, msg)    # 賦值給args是必須的,不然用戶沒法看到自定義異常的任何細節提示信息
        self.errno = errno
        self.msg = msg

raise NetworkError(1, 'No response')

使用繼承機制將異常組成一個層次結構:

class NetworkError(Exception): pass
    
class HostnameError(NetworkError): 
    pass
class TimeoutError(NetworkError): 
    pass

# 如下爲引起並捕捉自定義異常
try:
    raise TimeoutError('Time out')
except NetworkError as e:
    if type(e) is TimeoutError:
      pass

 

 

 

  (3)主動引起異常

 

Assert(斷言)

斷言的基本目的是:與其讓程序在在未來不知某個時候崩潰,不如在程序中不符合某個預判條件時,主動讓程序崩潰。 當斷言條件不知足時,會引起AssertionError異常。assert的格式爲:

assert condition [,msg]

其中,condition是一個表達式,其值若爲False時,assert語句就會引起AssertionError異常。 能夠在assert語句後添加字符串msg,用來在發生異常時,提示預先設置的字符串信息。

使用舉例:

age = -1
assert 0 < age < 100 'The age is fantastic.'  # 執行本句時,會引起AssertionError異常,並會提示:The age is fantastic.

assert語句常和 if __debug__語句一塊兒使用。在調試模式中,只讀變量__debug__的值爲True,能夠隨意地在代碼中加入assert和調試檢查。 在最優模式中(經過-O指定),__debug__爲False,將省略全部這些額外的檢查。

 

 

raise

使用raise語句可主動引起異常,raise語句的格式爲:

raise Exception([value])

若是raise語句沒有帶任何參數,將會再次引起最近一次生成的異常。

raise使用舉例:

raise KeyError('abc')   # 引起KeyError異常,並提示字符串 'abc'

 

 

 

  (4)捕捉異常

一般,使用捕捉異常的代碼結構要比使用多個if-else語句判斷來得更清晰,並且執行效率也幾乎沒什麼損失,故應該在程序中儘量使用try/except語句來查錯。 通常except語句捕捉並處理完異常後,程序將繼續執行跟在最後一個except代碼塊中的語句,程序並不會返回發生異常時的位置。

若是異常在函數內引起而未被處理,它就會向上傳遞到函數調用的地方,若是在那裏也沒有被處理,就會繼續向上傳遞,直到主程序(全局做用域)。 若是主程序裏也沒有處理,程序會帶着堆棧跟蹤中止。

若是須要,也能夠把未捕捉的異常傳遞給用戶自定義的函數sys.excepthook()進行處理。

 

● try-except結構:

捕捉單個異常:

try:
    x = 2/0
except ZeroDivisionError:
    print 'The divisor is zero'

捕捉多個異常:

# 方法一:
try:
    x = 2/'a'
except ZeroDivisionError:
    pass
except TypeError:
    pass
    
# 方法二:
try:
    x = 2/'a'
except (ZeroDivisionError, TypeError, NameError):   
    print('oops!')

捕捉全部異常:

try:
    x = 2/0
except Exception:   # 這裏使用基類Exception可捕捉除了「鍵盤中斷」和「程序退出」的全部異常
    pass

捕捉異常並訪問異常對象:

try:
    x = 2/0
except Exception as e:
    print(e)

這裏生成的異常實例e具備一些標準屬性,列舉以下:

e.args:引起異常時提供的參數元組,通常包含有描述該錯誤的字符串。

e.__cause__:使用顯式關聯異常時的前一個異常。

e.__context__:使用隱式關聯異常時的前一個異常。

e.__traceback__:與異常相關的跟蹤對象。

 

● try-except-else結構

當try中的語句沒有發生異常請跨下,運行else語句塊中內容。

示例:

try:
    f = open('foo', 'r')
except IOError as e:
    print(e)
else:
    data = f.read()
    f.close()

 

● try-except-finally結構

finally語句塊用於不管try是否有異常,都要運行的代碼。若是沒有引起異常,finally子句中的代碼將在try的代碼塊執行完後當即執行。 若是捕捉到了異常,則finally中的內容先運行,而後再運行except語句塊中的內容。

示例:

try:
    f = open('foo', 'r')
    data = f.read()    
except IOError as e:
    print(e)
finally:
    f.close()   # 不管前面發生什麼,都會關閉文件

 

● try-except-else-finally結構

else和finally也能夠組合在一塊兒使用。

示例:

try:
    f = open('foo', 'r')
except IOError as e:
    print(e)
else:
    data = f.read()
finally:
    f.close()

 

 

 (5)error模塊

errno模塊爲各類操做系統調用返回的整數錯誤代碼定義了符號名稱,這些整數代碼一般可在OSError(別名IOError)異常的errno屬性中找到。

os.strerror()函數能夠將整數的錯誤代碼轉換爲字符串信息。

errno模塊的errorcode字典,記錄了當前操做系統支持的錯誤代碼和POSIX符號名稱的對應關係,下表列舉了部分經常使用的錯誤代碼。

OSError中的錯誤代碼信息:

try:
    f = open(r'xxxxxx', 'r')
except Exception as e:
    print(e.errno)
    print(e.args)

# 運行結果爲:
2
(2, 'No such file or directory')

os.strerror()使用示例:

import os
import errno
print(os.strerror(2))

# 運行結果爲錯誤代碼2的字符串含義:No such file or directory'

 

常見POSIX錯誤代碼

錯誤代碼 名稱 描述
1 EPERM 操做未獲得許可
2 ENOENT 文件或目錄不存在
3 ESRCH 進程不存在
4 EINTR 系統調用被中斷
5 EIO I/O錯誤
6 ENXIO 設備或地址不存在
7 E2BIG 參數列表過長
8 ENOEXEC 訪問被拒絕
9 EBADF 錯誤的文件編號
10 ECHILD 無子進程
11 EAGAIN 再試
12 ENOMEM 內存不足
13 EACCESS 訪問被拒絕
14 EFAULT 錯誤的地址
15 ENOTBLK 須要塊設備
16 EBUSY 設備或資源方面
17 EEXIST 文件存在
18 EXDEV 跨設備連接
19 ENODEV 沒有這個設備
20 ENOTDIR 不是一個目錄
21 EISDIR 是一個目錄
22 EINVAL 無效參數
23 ENFILE 文件表溢出
24 EMFILE 打開文件過多
25 ENOTTY 不是一個終端
26 ETXTBSY 文本文件忙
27 EFBIG 文件過大
28 ENOSPC 設備上無剩餘空間
29 ESPIPE 非法尋址
30 EROFS 只讀文件系統
31 EMLINK 連接過多
32 EPIPE 管道已損壞
33 EDOM 數學參數在函數做用域以外
34 ERANGE 沒法表示的數學結果
35 EDEADLOCK 文件鎖定死鎖錯誤
36 ENAMETOOLONG 文件名過長
37 ENOLCK 無可用記錄鎖定
38 ENOSYS 函數沒法實現
39 ENOTEMPTY 目錄不爲空
40 ELOOP 遇到過多的符號連接
84 EILSEQ 非法的字節序列
85 ERESTART 中斷系統調用需重啓
86 ESTRPIPE 流管道錯誤
87 EUSERS 用戶過多
88 ENOTSOCK 非套接字上的套接字操做
89 EDESTADDRREQ 須要目的地址
90 EMSGSIZE 消息過長
91 EPROTOTYPE 套接字的協議類型錯誤
92 ENOPROTOOPT 協議不可用
93 EPROTONOSUPPORT 不支持協議
94 ESOCKTNOSUPPORT 套接字類型不受支持
95 ENOTSUP 操做被遠端支持
96 EPFNOSUPPORT 不支持協議族
97 EAFNOSUPPORT 協議不支持地址族
98 EADDRINUSE 地址已使用
99 EADDRNOTAVAIL 沒法分配請求的地址
100 ENETDOWN 網絡已關閉
101 ENETUNREACH 網絡不可到達
102 ENETRESET 網絡因爲重置中斷鏈接
103 ECONNABORTED 軟件致使鏈接中斷
104 ECONNRESET 對等端已將鏈接重置
105 ENOBUFS 無可用緩存空間
106 EISCONN 傳輸端點已經鏈接
107 ENOTCONN 傳輸端點未鏈接
108 ESHUTDOWN 沒法在傳輸端點關閉後發送
109 ETOOMANYREFS 引用過多:沒法鏈接
110 ETIMEDOUT 鏈接超時
111 ECONNREFUSED 鏈接被拒絕
112 EHOSTDOWN 主機已關閉
113 EHOSTUNREACH 無路由通向主機
114 EALREADY 操做已經在進行中
115 EINPROGRESS 操做正在進行
116 ESTALE 失效的NFS文件句柄
125 ECANCELED 操做取消
126 ENOKEY 無此鍵
127 EKEYEXPIRED 鍵過時
128 EKEYREVOKED 鍵被撤回
129 EKEYREJECTED 鍵被服務拒絕
130 EOWNERDEAD 擁有者已不存在
131 ENOTRECOVERABLE 狀態不可恢復
132 ERFKILL 操做因爲RF-KILL沒法進行

 

 

 (6)with語句

with語句支持在「上下文管理器」對象的控制下,執行一系列語句。經常使用於管理各類系統資源(如文件、鎖、鏈接等)。 當程序中發生異常,而致使脫離正常的釋放資源語句時,只要用了with,就能夠保證在離開with語句塊時自動釋放這些資源。下面是2個簡單的例子:

自動關閉文件對象

with open('a.txt', 'w') as f:
    f.write('xyz\n')
    ......
    f.write('done\n')
# 當程序離開with語句塊時,with語句會自動關閉已打開的文件

自動釋放鎖

import threading
lock = threading.Lock()
with lock:
    ......
# 當程序離開with語句塊時,with語句會自動釋放這個鎖

 

● with語句的通常語法:

with obj [as var]
    statements

obj對象須要實現__enter__()方法和__exit__()方法來支持with語句。當執行with obj語句時,會自動調用obj.__enter__()方法, 該方法的返回值將被放入指定變量var中。

當程序離開with語句塊時,會自動調用obj.__exit__()方法,其入參形式爲:__exit__(type, value, traceback), 三個入參分別爲:當前異常的類型、值、跟蹤信息。__exit__()方法返回True或False(表示被引起的異常是否獲得了處理)

如下爲一個用戶自定義類支持with的例子,這個類支持對已有列表進行一系列修改, 但這些修改只有在沒有發生異常時纔會生效,不然原始列表將保持不變。

class ListTransaction(object):
    def __init__(self, thelist):
        self.thelist = thelist
    def __enter__(self):
        self.workingcopy = list(self.thelist)
        return self.workingcopy
    def __exit__(self, type, value, tb):
        if type is None:
            self.thelist[:] = self.workingcopy
        return
        
# 使用with語句:
items = [1,2,3]
try:
    with ListTransaction(items) as working:
        working.append(4)
        working.append(5)
        raise RuntimeError()
except RuntimeError:
    pass
print(items)

# 因爲在離開with語句塊時發生了異常,所以__exit__()的入參type不爲None,最終結果爲:[1,2,3]

 

 

 

 

返回目錄

相關文章
相關標籤/搜索