本篇索引
(1)內置異常
(2)自定義異常
(3)主動引發異常
(4)捕捉異常
(5)error模塊
(6)with語句
(1)內置異常
● 異常的基類:
以下這些異常作爲具體異常的基類,都不會被顯式引發,但是可以使用它們捕捉某種錯誤。
基類名稱 | 說明 | |
---|---|---|
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]
● 使用 contextlib 模塊
使用 contextlib 模塊可以更加方便地編寫上例那樣的支持 With 上下文管理語句的類。 它提供了一個 @contextmanager 裝飾器,用戶使用它裝飾某個“函數”,達到上面類似的效果。 下面是一個使用 @contextmanager 裝飾器的例子:
from contextlib import contextmanager
@contextmanager
def ListTransaction(thelist):
workingcopy = list(thelist)
yield workingcopy
# 僅在沒有出現錯誤時,纔會修改原始列表
thelist[:] = workingcopy
上面的程序中,裝飾器將傳遞給 yield 的值用作了 __enter__() 方法的返回值(即提供給外部的 With 語句)。在退出 With (即調用 __exit__() 方法)時,執行將在 yield 語句後恢復 (即把原始的 thelist 列表的值改掉)。
如果 With 上下文中引發了異常,它將以異常形式出現在生成器函數中。