1.什么是異常
程序出現了錯誤(在編譯或者執行過程中)
>>> a
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
a
NameError: name 'a' is not defined
NameError 表示我們訪問了一個沒有初始化的變量. 在 Python 解釋器的符號表沒有找到那個
變量. 任何可訪問的變量必須在名稱空間里列出. 訪問變量需要由解釋器進行搜索,
如果請求的名字沒有在任何名稱空間里找到, 那么將會生成一個 NameError 異常.
>>> 1/0
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
1/0
ZeroDivisionError: division by zero
任何數值被零除都會導致一個 ZeroDivisionError
異常.
2.常見異常種類
異常名稱 | 描述 |
---|---|
BaseException | 所有異常的基類 |
SystemExit | 解釋器請求退出 |
KeyboardInterrupt | 用戶中斷執行(通常是輸入^C) |
Exception | 常規錯誤的基類 |
StopIteration | 迭代器沒有更多的值 |
GeneratorExit | 生成器(generator)發生異常來通知退出 |
StandardError | 所有的內建標准異常的基類 |
ArithmeticError | 所有數值計算錯誤的基類 |
FloatingPointError | 浮點計算錯誤 |
OverflowError | 數值運算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有數據類型) |
AssertionError | 斷言語句失敗 |
AttributeError | 對象沒有這個屬性 |
EOFError | 沒有內建輸入,到達EOF 標記 |
EnvironmentError | 操作系統錯誤的基類 |
IOError | 輸入/輸出操作失敗 |
OSError | 操作系統錯誤 |
WindowsError | 系統調用失敗 |
ImportError | 導入模塊/對象失敗 |
LookupError | 無效數據查詢的基類 |
IndexError | 序列中沒有此索引(index) |
KeyError | 映射中沒有這個鍵 |
MemoryError | 內存溢出錯誤(對於Python 解釋器不是致命的) |
NameError | 未聲明/初始化對象 (沒有屬性) |
UnboundLocalError | 訪問未初始化的本地變量 |
ReferenceError | 弱引用(Weak reference)試圖訪問已經垃圾回收了的對象 |
RuntimeError | 一般的運行時錯誤 |
NotImplementedError | 尚未實現的方法 |
SyntaxError | Python 語法錯誤 |
IndentationError | 縮進錯誤 |
TabError | Tab 和空格混用 |
SystemError | 一般的解釋器系統錯誤 |
TypeError | 對類型無效的操作 |
ValueError | 傳入無效的參數 |
UnicodeError | Unicode 相關的錯誤 |
UnicodeDecodeError | Unicode 解碼時的錯誤 |
UnicodeEncodeError | Unicode 編碼時錯誤 |
UnicodeTranslateError | Unicode 轉換時錯誤 |
Warning | 警告的基類 |
DeprecationWarning | 關於被棄用的特征的警告 |
FutureWarning | 關於構造將來語義會有改變的警告 |
OverflowWarning | 舊的關於自動提升為長整型(long)的警告 |
PendingDeprecationWarning | 關於特性將會被廢棄的警告 |
RuntimeWarning | 可疑的運行時行為(runtime behavior)的警告 |
SyntaxWarning | 可疑的語法的警告 |
UserWarning | 用戶代碼生成的警告 |
3.異常處理
簡單介紹:
s = 'Hello girl!'
print s[100]
print 'continue'
如果我們沒有對異常進行任何預防,那么在程序執行的過程中發生異常,就會中斷程序,調用python默認的異常處理器,並在終端輸出異常信息。這種情況下,第3行代碼不會執行。
try…except
s = 'Hello girl!'
try:
print s[100]
except IndexError:
print 'error...'
print 'continue'
程序執行到第2句時發現try語句,進入try語句塊執行,發生異常,回到try語句層,尋找后面是否有except語句。找到except語句后,會調用這個自定義的異常處理器。except將異常處理完畢后,程序繼續往下執行。這種情況下,最后兩個print語句都會執行。
except后面也可以為空,表示捕獲任何類型的異常。
try…finally
s = 'Hello girl!'
try:
print s[100]
finally:
print 'error...'
print 'continue'
finally語句表示,無論異常發生與否,finally中的語句都要執行。但是,由於沒有except處理器,finally執行完畢后程序便中斷。這種情況下,倒第2個print會執行,到第1個不會執行。如果try語句中沒有異常,三個print都會執行。
assert
assert False,'error...'
print 'continue'
這個語句,先判斷assert后面緊跟的語句是True還是False,如果是True則繼續執行print,如果是False則中斷程序,調用默認的異常處理器,同時輸出assert語句逗號后面的提示信息。本例情況下,程序中斷,提示error,后面的print不執行。
with…as
with open('nothing.txt','r') as f:
f.read()
print 2/0
print 'continue'
我們平時在使用類似文件的流對象時,使用完畢后要調用close方法關閉,很麻煩。這里with…as語句提供了一個非常方便的替代方法:open打開文件后將返回的文件流對象賦值給f,然后在with語句塊中使用。with語句塊完畢之后,會隱藏地自動關閉文件。
如果with語句或語句塊中發生異常,會調用默認的異常處理器處理,但文件還是會正常關閉。
這種情況下,會拋出異常,最后的print不執行。
詳細介紹
可以通過編程來選擇處理部分異常。看一下下面的例子,它會一直要求用戶輸入直到輸入一個合法的整數為止,但允許用戶中斷這個程序(使用Control-C或系統支持的任何方法);注意用戶產生的中斷引發的是 KeyboardInterrupt 異常。
>>> while True: ... try: ... x = int(raw_input("Please enter a number: ")) ... break ... except ValueError: ... print "Oops! That was no valid number. Try again..." ...
Try語句按以下方式工作。
- 首先,執行try 子句(try和except關鍵字之間的語句)。
- 如果未發生任何異常,忽略except 子句且try語句執行完畢。
- 如果在 try 子句執行過程中發生異常,跳過該子句的其余部分。如果異常的類型與except關鍵字后面的異常名匹配, 則執行 except 子句,然后繼續執行try語句之后的代碼。
- 如果異常的類型與 except 關鍵字后面的異常名不匹配,它將被傳遞給上層的try語句;如果沒有找到處理這個異常的代碼,它就成為一個未處理異常,程序會終止運行並顯示一條如上所示的信息。
Try語句可能有多個異常子句,用來指定多個不同的異常。不過至多只有一個處理程序將被執行。處理程序只處理發生在相應 try 子句中的異常,不會處理同一個try子句的其他處理程序中發生的異常。一個 except 子句可以用帶括號的元組列出多個異常的名字,例如:
... except (RuntimeError, TypeError, NameError): ... pass
注意,此元組周圍的括號是必需的,因為except ValueError, e:是舊式的寫法,在現代 Python 中通常寫成 except ValueError as e: (如下所述)。為了保持向后兼容性,舊式語法仍然是支持的。這意味着except RuntimeError, TypeError不等同於except (RuntimeError, TypeError): 而等同於except RuntimeError as TypeError: , 這應該不是你想要的。
最后一個 except 子句可以省略異常名稱,以當作通配符使用。使用這種方式要特別小心,因為它會隱藏一個真實的程序錯誤!它還可以用來打印一條錯誤消息,然后重新引發異常 (讓調用者也去處理這個異常):
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as e: print "I/O error({0}): {1}".format(e.errno, e.strerror) except ValueError: print "Could not convert data to an integer." except: print "Unexpected error:", sys.exc_info()[0] raise
try...except語句有一個可選的else 子句,其出現時,必須放在所有 except 子句的后面。如果需要在 try 語句沒有拋出異常時執行一些代碼,可以使用這個子句。例如:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
使用else子句比把額外的代碼放在try子句中要好,因為它可以避免意外捕獲不是由try ... except語句保護的代碼所引發的異常。
當異常發生時,它可能帶有相關數據,也稱為異常的參數。參數的有無和類型取決於異常的類型。
except 子句可以在異常名(或元組)之后指定一個變量。這個變量將綁定於一個異常實例,同時異常的參數將存放在實例的args中。為方便起見,異常實例定義了__str__() ,因此異常的參數可以直接打印而不必引用.args。
也可以在引發異常之前先實例化一個異常,然后向它添加任何想要的屬性。
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print type(inst) # the exception instance ... print inst.args # arguments stored in .args ... print inst # __str__ allows args to be printed directly ... x, y = inst.args ... print 'x =', x ... print 'y =', y ... <type 'exceptions.Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
對於未處理的異常,如果它含有參數,那么參數會作為異常信息的最后一部分打印出來。
異常處理程序不僅處理直接發生在 try 子句中的異常,而且還處理 try 子句中調用的函數(甚至間接調用的函數)引發的異常。例如:
>>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as detail: ... print 'Handling run-time error:', detail ... Handling run-time error: integer division or modulo by zero
引發異常
raise語句允許程序員強行引發一個指定的異常。例如:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: HiThere
raise的唯一參數指示要引發的異常。它必須是一個異常實例或異常類(從Exception派生的類)。
如果你確定需要引發異常,但不打算處理它,一個簡單形式的raise語句允許你重新引發異常:
>>> try: ... raise NameError('HiThere') ... except NameError: ... print 'An exception flew by!' ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in ? NameError: HiThere
用戶定義的異常
程序可以通過創建新的異常類來命名自己的異常(Python 類的更多內容請參見類)。異常通常應該繼承Exception類,直接繼承或者間接繼承都可以。例如:
>>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return repr(self.value) ... >>> try: ... raise MyError(2*2) ... except MyError as e: ... print 'My exception occurred, value:', e.value ... My exception occurred, value: 4 >>> raise MyError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in ? __main__.MyError: 'oops!'
在此示例中,Exception默認的__init__()被覆蓋了。新的行為簡單地創建了value 屬性。這將替換默認的創建args 屬性的行為。
異常類可以像其他類一樣做任何事情,但是通常都會比較簡單,只提供一些屬性以允許異常處理程序獲取錯誤相關的信息。創建一個能夠引發幾種不同錯誤的模塊時,一個通常的做法是為該模塊定義的異常創建一個基類,然后基於這個基類為不同的錯誤情況創建特定的子類:
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expr -- input expression in which the error occurred msg -- explanation of the error """ def __init__(self, expr, msg): self.expr = expr self.msg = msg class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: prev -- state at beginning of transition next -- attempted new state msg -- explanation of why the specific transition is not allowed """ def __init__(self, prev, next, msg): self.prev = prev self.next = next self.msg = msg
大多數異常的名字都以"Error"結尾,類似於標准異常的命名。
很多標准模塊中都定義了自己的異常來報告在它們所定義的函數中可能發生的錯誤。類 這一章給出了類的詳細信息。
定義清理操作
Try語句有另一個可選的子句,目的在於定義必須在所有情況下執行的清理操作。例如:
>>> try: ... raise KeyboardInterrupt ... finally: ... print 'Goodbye, world!' ... Goodbye, world! KeyboardInterrupt
不管有沒有發生異常,在離開try語句之前總是會執行finally 子句。當try子句中發生了一個異常,並且沒有except字句處理(或者異常發生在except或else子句中),在執行完finally子句后將重新引發這個異常。try語句由於break、contine或return語句離開時,同樣會執行finally子句。以下是一個更復雜些的例子 (同時有except和finally字句的try語句的工作方式與 Python 2.5 一樣):
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print "division by zero!" ... else: ... print "result is", result ... finally: ... print "executing finally clause" ... >>> divide(2, 1) result is 2 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
正如您所看到的,在任何情況下都會執行finally子句。由兩個字符串相除引發的 TypeError異常沒有被except子句處理,因此在執行finally子句后被重新引發。
在真實的應用程序中, finally子句用於釋放外部資源(例如文件或網絡連接),不管資源的使用是否成功。
清理操作的預定義
有些對象定義了在不需要該對象時的標准清理操作,無論該對象的使用是成功還是失敗。看看下面的示例,它嘗試打開一個文件並打印其內容到屏幕。
for line in open("myfile.txt"): print line,
這段代碼的問題就是代碼執行完之后它還會讓文件在一段不確定的時間內保持打開狀態。這在簡單的腳本中沒什么,但是在大型應用程序中可能是一個問題。 With語句可以確保像文件這樣的對象總能及時准確地被清理掉。
with open("myfile.txt") as f: for line in f: print line,
執行該語句后,文件f 將始終被關閉,即使在處理某一行時遇到了問題。其它對象是否提供了預定義的清理行為要查看它們的文檔。