1. 程式人生 > 程式設計 >Python新手學習raise用法

Python新手學習raise用法

當程式出現錯誤時,系統會自動引發異常。除此之外,Python 也允許程式自行引發異常,自行引發異常使用 raise 語句來完成。

很多時候,系統是否要引發異常,可能需要根據應用的業務需求來決定,如果程式中的資料、執行與既定的業務需求不符,這就是一種異常。由於與業務需求不符而產生的異常,必須由程式設計師來決定引發,系統無法引發這種異常。

如果需要在程式中自行引發異常,則應使用 raise 語句。raise 語句有如下三種常用的用法:

  1. raise:單獨一個 raise。該語句引發當前上下文中捕獲的異常(比如在 except 塊中),或預設引發 RuntimeError 異常。
  2. raise 異常類:raise 後帶一個異常類。該語句引發指定異常類的預設例項。
  3. raise 異常物件:引發指定的異常物件。

上面三種用法最終都是要引發一個異常例項(即使指定的是異常類,實際上也是引發該類的預設例項),raise 語句每次只能引發一個異常例項。

可以利用 raise 語句再次改寫前面五子棋遊戲中處理使用者輸入的程式碼:

try :
  #將使用者輸入的字串以逗號( ,)作為分隔符,分隔成兩個字串
  x_str,y_str = inputStr.split(sep =",")
  #如果要下棋的點不為空
  if board[int(y_str) - 1] [int(x_str) - 1] != "+":
  #引發預設的RuntimeError異常
  raise
  #把對應的列表元素賦為”●”
  board [int(y_str) - 1] [int(x_str) - 1] = ”●”
except Exception as e:
  print (type(e))
  inputStr = input("您輸入的座標不合法,請重新輸入,下棋座標應以x,y 的格式\n")
  continue

上面程式中第 7 行程式碼使用 raise 語句來自行引發異常,程式認為當用戶試圖向一個已有棋子的座標點下棋時就是異常。當 Python 直譯器接收到開發者自行引發的異常時,同樣會中止當前的執行流,跳到該異常對應的 except 塊,由該 except 塊來處理該異常。也就是說,不管是系統自動引發的異常,還是程式設計師於動引發的異常,Python 直譯器對異常的處理沒有任何差別。

即使是使用者自行引發的異常,也可以使用 try except 來捕獲它。當然也可以不管它,讓該異常向上(先呼叫者)傳播,如果該異常傳到 Python 直譯器,那麼程式就會中止。

下面示例示範了處理使用者引發異常的兩種方式:

def main():
  try:
    # 使用try...except來捕捉異常
    # 此時即使程式出現異常,也不會傳播給main函式
    mtd(3)
  except Exception as e:
    print('程式出現異常:',e)
  # 不使用try...except捕捉異常,異常會傳播出來導致程式中止
  mtd(3)
def mtd(a):
  if a > 0:
    raise ValueError("a的值大於0,不符合要求")
main()

從上面程式可以看到,程式既可在呼叫 mtd(3) 時使用 try except 來捕獲異常,這樣該異常將會被 except 塊捕獲,不會傳播給呼叫它的函式;也可直接呼叫 mtd(3),這樣該函式的異常就會直接傳播給它的呼叫函式,如果該函式也不處理該異常,就會導致程式中止。

執行上面程式,可以看到如下輸出結果:

程式出現異常: a的值大於0,不符合要求
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\1.py",line 13,in <module>
main()
File "C:\Users\mengma\Desktop\1.py",line 9,in main
mtd(3)
File "C:\Users\mengma\Desktop\1.py",line 12,in mtd
raise ValueError("a的值大於0,不符合要求")
ValueError: a的值大於0,不符合要求

上面第一行輸出是第一次呼叫 mtd (3) 的結果,該方法引發的異常被 except 塊捕獲並處理。後面的大段輸出則是第二次呼叫 mtd(3) 的結果,由於該異常沒有被 except 塊捕獲,因此該異常一直向上傳播,直到傳給 Python 直譯器導致程式中止。

第二次呼叫 mtd(3) 引發的以“File”開頭的三行輸出,其實顯示的就是異常的傳播軌跡資訊。也就是說,如果程式不對異常進行處理,Python 預設會在控制檯輸出異常的傳播軌跡資訊。

自定義異常類

很多時候,程式可選擇引發自定義異常,因為異常的類名通常也包含了該異常的有用資訊。所以在引發異常時,應該選擇合適的異常類,從而可以明確地描述該異常情況。在這種情形下,應用程式常常需要引發自定義異常。

使用者自定義異常都應該繼承 Exception 基類或 Exception 的子類,在自定義異常類時基本不需要書寫更多的程式碼,只要指定自定義異常類的父類即可。

下面程式建立了一個自定義異常類(程式一):

class AuctionException(Exception): pass

上面程式建立了 AuctionException 異常類,該異常類不需要類體定義,因此使用 pass 語句作為佔位符即可。

在大部分情況下,建立自定義異常類都可採用與程式一相似的程式碼來完成,只需改變 AuctionException 異常的類名即可,讓該異常的類名可以準確地描述該異常。

except 和 raise 同時使用

在實際應用中對異常可能需要更復雜的處理方式。當一個異常出現時,單靠某個方法無法完全處理該異常,必須由幾個方法協作才可完全處理該異常。也就是說,在異常出現的當前方法中,程式只對異常進行部分處理,還有些處理需要在該方法的呼叫者中才能完成,所以應該再次引發異常,讓該方法的呼叫者也能捕獲到異常。

為了實現這種通過多個方法協作處理同一個異常的情形,可以在 except 塊中結合 raise 語句來完成。如下程式示範了except 和 raise 同時使用的方法:

class AuctionException(Exception): pass
class AuctionTest:
  def __init__(self,init_price):
    self.init_price = init_price
  def bid(self,bid_price):
    d = 0.0
    try:
      d = float(bid_price)
    except Exception as e:
      # 此處只是簡單地列印異常資訊
      print("轉換出異常:",e)
      # 再次引發自定義異常
      raise AuctionException("競拍價必須是數值,不能包含其他字元!") # ①
      raise AuctionException(e)
    if self.init_price > d:
      raise AuctionException("競拍價比起拍價低,不允許競拍!")
    initPrice = d
def main():
  at = AuctionTest(20.4)
  try:
    at.bid("df")
  except AuctionException as ae:
    # 再次捕獲到bid()方法中的異常,並對該異常進行處理
    print('main函式捕捉的異常:',ae)
main()

上面程式中 9~13 行程式碼對應的 except 塊捕獲到異常後,系統列印了該異常的字串資訊,接著引發一個 AuctionException 異常,通知該方法的呼叫者再次處理該 AuctionException 異常。所以程式中的 main() 函式,也就是 bid() 方法的呼叫者還可以再次捕獲 AuctionException 異常,井將該異常的詳細描述資訊打印出來。

這種 except 和 raise 結合使用的情況在實際應用中非常常用。實際應用對異常的處理通常分成兩個部分:

應用後臺需要通過日誌來記錄異常發生的詳細情況;

應用還需要根據異常向應用使用者傳達某種提示;

在這種情形下,所有異常都需要兩個方法共同完成,也就必須將 except 和 raise 結合使用。

如果程式需要將原始異常的詳細資訊直接傳播出去,Python 也允許用自定義異常對原始異常進行包裝,只要將上面 ① 號程式碼改為如下形式即可:

raise AuctionException(e)

raise 不需要引數

正如前面所看到的,在使用 raise 語句時可以不帶引數,此時 raise 語句處於 except 塊中,它將會自動引發當前上下文啟用的異常;否則,通常預設引發 RuntimeError 異常。

例如,將上面程式改為如下形式:

class AuctionException(Exception): pass
class AuctionTest:
  def __init__(self,e)
      # 再次引發自定義異常
      raise
    if self.init_price > d:
      raise AuctionException("競拍價比起拍價低,不允許競拍!")
    initPrice = d
def main():
  at = AuctionTest(20.4)
  try:
    at.bid("df")
  except AuctionException as ae:
    # 再次捕獲到bid()方法中的異常,並對該異常進行處理
    print('main函式捕捉的異常:',ae)
main()

正如從 13 行程式碼所看到的,此時程式在 except 塊中只是簡單地使用 raise 語句來引發異常,那麼該 raise 語句將會再次引發該 except 塊所捕獲的異常。執行該程式,可以看到如下輸出結果:

轉換出異常: could not convert string to float: 'df'

main 函式捕獲的異常:<class 'ValueError'>

知識點補充:

演示raise用法

try:
   s = None
   if s is None:
     print "s 是空物件"
     raise NameError   #如果引發NameError異常,後面的程式碼將不能執行
   print len(s) #這句不會執行,但是後面的except還是會走到
except TypeError:
   print "空物件沒有長度"
 
s = None
if s is None:
  raise NameError 
print 'is here?' #如果不使用try......except這種形式,那麼直接丟擲異常,不會執行到這裡

觸發異常

我們可以使用raise語句自己觸發異常

raise語法格式如下:

raise [Exception [,args [,traceback]]]

語句中 Exception 是異常的型別(例如,NameError)引數標準異常中任一種,args 是自已提供的異常引數。

最後一個引數是可選的(在實踐中很少使用),如果存在,是跟蹤異常物件。到此這篇關於Python新手學習raise用法的文章就介紹到這了,更多相關Python中raise用法內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!