異常

# 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的習慣。

相關文章
相關標籤/搜索