# coding:utf-8網絡
#什麼是異常app
#Python用異常對象來表示異常狀況。遇到錯誤後,會引起異常。若是異常對象並未被處理或捕捉,程序就會用所謂的回溯(traceback,一種錯誤信息)終止執行。ide
1/0函數
#Traceback (most recent call last):學習
# File "<input>", line 1, in <module>spa
#ZeroDivisionError: integer division or modulo by zero對象
#事實上,每一個異常都是一些類(本例中是ZeroDivisionError)的實例,這些實例能夠被引起,而且能夠用不少種方法進行捕捉,使得程序能夠捉住錯誤而且對其進行處理,而不是讓整個程序失效。繼承
#按本身的方式出錯索引
#在學習如何處理異常以前,先看一下本身如何引起異常,以及建立本身的異常類型。ip
# raise語句
#爲了引起異常,可使用一個類(應該是Exception的子類)或者實例參數調用raise語句。
raise Exception
#Traceback (most recent call last):
# File "<input>", line 1, in <module>
#Exception
raise Exception('hyperdrive overload')
#Traceback (most recent call last):
# File "<input>", line 1, in <module>
#Exception: hyperdrive overload
#第一個例子raise Exception引起了一個沒有任何有關錯誤信息的普通異常。後一個例子中,則添加了錯誤信息hyperdrive overload。
#內建的異常種類有不少。這些內建異常均可以在exceptions模塊(和內建的命名空間)中找到。可使用dir函數列出模塊的內容。
import exceptions
dir(exceptions)
#這個名單會很長,全部這些異常均可以用在raise語句中:
raise ArithmeticError
#Traceback (most recent call last):
# File "<input>", line 1, in <module>
#ArithmeticError
#一些最重要的內建異常類:
#Exception 全部異常的基類
#AttributeError 特性引用或賦值失敗時引用
#IOError 試圖打開不存在文件(包括其餘狀況)時引起
#IndexError 在使用序列中不存在的索引時引起
#KeyError 在使用映射中不存在的鍵時引起
#NameError 在找不到名字(變量)時引起
#SyntaxError 在代碼爲錯誤形式時引起
#TypeError 在內建操做或者函數應用於錯誤類型的對象時引起
#ValueError 在內建操做或者函數應用於正確類型的對象,可是該對象使用不合適的值時引起
#ZeroDivisionError 在除法或者摸除操做的第二個參數爲0時引起
#自定義異常類
#如何建立本身的異常類?就像其餘類同樣,只是要確保從Exception類繼承(不論是間接的或者是直接的,也就是說繼承其餘的內建異常類也是能夠的)。
#那麼編寫一個自定義異常類基本上就像下面這樣:
#Class SomeCustomException(Exception): pass
#捕捉異常
#可使用try/except語句來實現
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x / y
#程序工做正常,假如用戶輸入0做爲第二個數:
#Enter the first number: >? 10
#Enter the second number: >? 0
#Traceback (most recent call last):
# File "<input>", line 3, in <module>
#ZeroDivisionError: integer division or modulo by zero
#爲了捕捉異常而且作出一些錯誤處理,能夠這樣重寫程序:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y
except ZeroDivisionError:
print "The second number can't be zero!"
#Enter the first number: >? 10
#Enter the second number: >? 0
#The second number can't be zero!
#其實也能夠用if語句檢查y值會簡單些,但若是須要給程序假如更多除法,那麼就得給每一個除法加個if語句。而使用try/except的話只須要一個錯誤處理器。
#打開和關閉屏蔽異常
#考慮一下一個能「屏蔽」ZeroDivisionError(除零錯誤)的計算器類。若是這個行爲被激活,那麼計算器就會打印錯誤信息,而不是讓異常傳播。
class MuffledCalculator:
muffled = False #異常屏蔽的開關
def calc(self, expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled: #判斷開關,是否要打開異常仍是自定義錯誤信息
print 'Division by zero is illegal' #自定義錯誤信息
else:
raise #異常
calculator = MuffledCalculator()
print calculator.calc('10/2')
#5
calculator.muffled = False
calculator.calc('10/0')
#Traceback (most recent call last):
# File "<input>", line 2, in <module>
# File "<input>", line 5, in calc
# File "<string>", line 1, in <module>
#ZeroDivisionError: integer division or modulo by zero
calculator.muffled = True
calculator.calc('10/0')
#Division by zero is illegal
#不止一個except子句
#若是運行上一節的程序而且在提示符後面輸入非數字類型的值,就會產生另一個異常:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x / y
#Enter the first number: >? 10
#Enter the second number: >? "Hello, world!"
#Traceback (most recent call last):
# File "<input>", line 3, in <module>
#TypeError: unsupported operand type(s) for /: 'int' and 'str'
#由於except子句只尋找ZeroDivisionError異常,此次的錯誤就溜過了檢查並致使程序終止
#爲了捕捉這個異常,能夠直接在同一個try/except語句後面加上另外一個except子句:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y
except ZeroDivisionError:
print "The second number can't be zero!"
except TypeError:
print "That wasn't a number, was it?"
#Enter the first number: >? 10
#Enter the second number: >? "Hello world!"
#That wasn't a number, was it?
#此次用if語句實現可就複雜了,應該注意到,異常處理並不會搞亂原來的代碼,而增長一大堆if語句檢查可能的錯誤狀況會讓代碼至關難讀。
#用一個塊捕捉兩個異常
#若是須要用一個塊捕捉多個類型異常,那麼能夠將它們做爲元組列出,像下面這樣:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y
except (ZeroDivisionError, TypeError, NameError):
print 'Your numbers were bogus...'
#Enter the first number: >? 10
#Enter the second number: >? 0
#Your numbers were bogus...
#上面代碼中,若是用戶輸入字符串或者其餘類型的值,而不是數字,或者第2個數爲0,都會打印一樣的錯誤信息。
#捕捉對象
#若是但願在except子句中訪問異常對象,可使用參數(注意,就算要捕捉到多個異常,也只需向except子句提供一個參數---一個元組)。
#好比想讓程序繼續運行,可是又由於某種緣由想記錄下錯誤(好比只是打印給用戶看),這個功能就頗有用。
#下面的示例程序會打印異常(若是發生的話),可是程序會繼續運行:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y
except (ZeroDivisionError, TypeError), e:
print e
#Enter the first number: >? 10
#Enter the second number: >? 0
#integer division or modulo by zero
#Enter the first number: >? 10
#Enter the second number: >? 'Hello'
#unsupported operand type(s) for /: 'int' and 'str'
#真正的捕捉
#就算程序能處理好幾種類型的異常,但有些異常還會從眼皮底下溜走。好比還用那個除法程序來舉例,在提示符下面直接按回車,不輸入任何東西,會獲得一個相似下面這樣的錯誤信息。
#Traceback (most recent call last):
# File "<input>", line 2, in <module>
# File "<string>", line 0
# ^
#SyntaxError: unexpected EOF while parsing
#若是真的想用一段代碼捕捉全部異常,那麼能夠在except子句中忽略全部的異常類:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
print x/y
except:
print 'Something wrong happend...'
#Enter the first number: >? 'Hello'
#Enter the second number: >? '123'
#Something wrong happend...
#else子句
#能夠對條件和循環語句那樣,給try/except語句加個else子句:
try:
print 'A simple task'
except:
print 'What? Something went wrong?'
else:
print 'Ah...It went as planned.'
#執行結果:
#A simple task
#Ah...It went as planned.
#加入到循環語句
while True:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
value = x/y
print 'x/y is', value
except:
print 'Invalid input. Please try again.'
else:
break
#這裏的循環只在沒有異常引起的狀況下才會退出(由else子句中的break語句退出)。
#Enter the first number: >? 1
#Enter the second number: >? 0
#Invalid input. Please try again.
#Enter the first number: >? 'foo'
#Enter the second number: >? 'bar'
#Invalid input. Please try again.
#Enter the first number: >? baz
#Invalid input. Please try again.
#Enter the first number: >? 10
#Enter the second number: >? 2
#x/y is 5
#以前提到過,可使用空的except子句來捕捉全部Exception類的異常(也會捕捉其全部子類的異常)。百分之百捕捉到全部的異常是不可能的,由於try/except語句中的代碼可能會出問題。
#不過若是使用excpet Exception的話,可使用以前除法程序中打印更加有用的錯誤信息:
while True:
try:
x = input('Enter the first number: ')
y = input('Enter the second number: ')
value = x/y
print 'x/y is', value
except Exception, e:
print 'Invalid input:', e
print 'Please try again'
else:
break
#Enter the first number: >? 1
#Enter the second number: >? 0
#Invalid input: integer division or modulo by zero
#Please try again
#Enter the first number: >? 'x'
#Enter the second number: >? 'y'
#Invalid input: unsupported operand type(s) for /: 'str' and 'str'
#Please try again
#Enter the first number: >? quuux
#Invalid input: name 'quuux' is not defined
#Please try again
#Enter the first number: >? 10
#Enter the second number: >? 2
#x/y is 5
#finally子句
#它能夠用來在可能異常後進行清理。它和try子句聯合使用:
x = None
try:
x = 1/0 #這句會報錯
finally: #不會由於錯誤終止,會繼續執行下面語句
print 'Cleaning up...'
del x
#上面代碼中,finally子句確定會被執行,無論try子句中是否發生異常。
#在try子句以前,初始化x的緣由是若是不這樣作,因爲ZeroDivisionError的存在,x就永遠不會被賦值。這樣就會致使在finally子句中使用del刪除它的時候異常,並且這個異常是沒法捕捉的。
#運行這段代碼,在程序崩潰以前,對於變量x的清理就完成了:
#Cleaning up...
#Traceback (most recent call last):
# File "<input>", line 3, in <module>
#ZeroDivisionError: integer division or modulo by zero
#由於使用del語句刪除一個變量是很是不負責的清理手段,因此finally子句用於關閉文件或者網絡套接字時會很是有用。
#還能夠在同一條語句中組合使用try,except,finally和else。
try:
1/0
except NameError:
print "Unknown variable"
else:
print "That went well!"
finally:
print "Cleaning up."
#異常和函數
#異常和函數能很天然的一塊兒工做。若是異常在函數內引起而不被處理,它會傳播至函數調用的地方。若是在那裏也沒有處理異常,它會繼續傳播,一直到達主程序(全局做用域)。若是那裏沒有異常處理程序,程序會帶着棧跟蹤停止。
#例子:
def faulty():
raise Exception('Something is wrong')
def ignore_exception():
faulty()
def handle_exception():
try:
faulty()
except:
print 'Exception handled'
ignore_exception()
#Traceback (most recent call last):
# File "<input>", line 11, in <module>
# File "<input>", line 4, in ignore_exception
# File "<input>", line 2, in faulty
#Exception: Something is wrong
#faulty中產生的異常經過faulty和ignore_exception傳播,最終致使了棧跟蹤。
handle_exception()
#Exception handled
#它傳播到handle_exception,但在這個函數中被try/except語句處理。
#異常之禪
#有些時候,條件語句能夠實現和異常處理一樣的功能,可是條件語句可能在天然性和可讀性上差些。而從另外一方面看,某些程序中使用if/else實現會比用try/except要好。
#讓咱們看看幾個例子。
#假設有一個字典,咱們但願打印出存儲在特定的鍵下面的值。若是該鍵不存在,那麼什麼也不做。代碼可能像下面這樣寫:
def describePerson(person):
print 'Description of', person['name']
print 'Age:', person['age']
if 'occupation' in person:
print 'Occupation:', person['occupation']
#若是給程序提供包含名字tom和年齡35(沒有職業)的字典的函數
tom = {'name':'tom', 'age':'35'}
describePerson(tom)
#會獲得以下輸出:
#Description of tom
#Age: 35
#若是添加了職業IT
tom = {'name':'tom', 'age':'35','occupation':'IT'}
describePerson(tom)
#會獲得以下輸出:
#Description of tom
#Age: 35
#Occupation: IT
#代碼很是直觀,可是效果不高。程序會兩次查找'occupation'鍵,其中一次用來查看鍵是否存在,另一次得到值(打印)。另一個解決方案以下:
def describePerson(person):
print 'Description of', person['name']
print 'Age:', person['age']
try:
print 'Occupation:' + person['occupation']
except KeyError: pass
#這個程序直接假定'occupation'鍵存在。若是它的確存在,直接取出它的值打印輸出便可---不用額外檢查它是否真的存在。
tom = {'name':'tom', 'age':'35'}
describePerson(tom)
tom = {'name':'tom', 'age':'35','occupation':'IT'}
describePerson(tom)
#在查看對象是否存在特定特性時,try/except也頗有用。假設想要查看某對象是否有write特性,那麼可使用以下代碼:
try:
obj.write
except AttributeError:
print 'The object is not writeable'
else:
print 'The object is writeable'
#這裏的try子句僅僅訪問特性而不用對它作別的有用的事情。若是AttributeError異常引起,就證實對象沒有這個特性,反之存在該特性。
#在不少狀況下,使用try/except語句比使用if/else會更天然一些,應該養成儘量使用try/except的習慣。