天天看點

異常

預覽

try語句按照如下方式工作;

  • 首先,執行try子句(在關鍵字try和關鍵字except之間的語句)
  • 如果沒有異常發生,忽略except子句,try子句執行後結束。
  • 如果在執行try子句的過程中發生了異常,那麼try子句餘下的部分将被忽略。如果異常的類型和 except 之後的名稱相符,那麼對應的except子句将被執行。最後執行 try 語句之後的代碼。
  • 如果一個異常沒有與任何的except比對,那麼這個異常将會傳遞給上層的try中。

一個 try 語句可能包含多個except子句,分别來處理不同的特定的異常。最多隻有一個分支會被執行。

處理程式将隻針對對應的try子句中的異常進行處理,而不是其他的 try 的處理程式中的異常。

一個except子句可以同時處理多個異常,這些異常将被放在一個括号裡成為一個元組,

Python 中(至少)有兩種錯誤:文法錯誤和異常(syntax errors 和 exceptions)

文法錯誤,也被稱作解析錯誤

while True print('Hello world')
           
File "<ipython-input-2-614901b0e5ee>", line 1
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax
           

文法分析器指出錯誤行,并且在檢測到錯誤的位置前面顯示一個小“箭頭”。 錯誤是由箭頭前面的标記引起的(或者至少是這麼檢測的)

5/0
           
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-1-67a69f72677d> in <module>()
----> 1 5/0


ZeroDivisionError: division by zero
           
try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide zero!")
           
You can't divide zero!
           
print("Give me two numbers,and I'll divide them.")
print("Enter'q' to quit.")

while True:
    first_number = input('\nFist number: ')
    if first_number == 'q':
        break
    second_number = input('\nsecond number: ')
    if second_number == 'q':
        break
    answer = int(first_number) / int(second_number)
    print(answer)
           
Give me two numbers,and I'll divide them.
Enter'q' to quit.

Fist number: 5

second number: 0



---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-4-c303f6ef9aca> in <module>()
      9     if second_number == 'q':
     10         break
---> 11     answer = int(first_number) / int(second_number)
     12     print(answer)


ZeroDivisionError: division by zero
           
# 異常
print("Give me two numbers,and I'll divide them.")
print("Enter'q' to quit.")

while True:
    first_number = input('\nFist number: ')
    if first_number == 'q':
        break
    second_number = input('\nsecond number: ')
    if second_number == 'q':
        break
    try:
        answer = int(first_number) / int(second_number)
    except ZeroDivisionError:
        print("You can't divide zero!")
    else:
        print(answer)
           
Give me two numbers,and I'll divide them.
Enter'q' to quit.

Fist number: 5

second number: 0
You can't divide zero!

Fist number: 5

second number: 3
1.6666666666666667

Fist number: q
           

異常

異常:即程式運作時遇到非正常的情況,如符号錯誤,邏輯錯誤,文法錯誤等等。

python語言使用異常對象(exception object)來表示異常情況,由于它是一種面向對象語言,是以程式中抛出的異常也是一種類,所有的異常都是從基類Exception繼承而來,而且是在Exceptions子產品中被定義。Python将所有異常名稱放在内建的命名空間中,使用者在使用異常時不需要導入任何子產品。若異常出現時程式沒有進行捕捉或處理,在程式中會使用回溯(Traceback)來終止執行。

異常有兩種激活方式:

  • 在程式運作出錯時自動引發
  • 程式員自己引發,即抛出異常

抛出異常

抛出異常往往是程式自動根據錯誤特征進行自動識别和抛出,在python中可以使用raise語句強制抛出異常。

raise語句

當程式員想要抛出一個異常時,直接在raise語句中指明錯誤或異常的名稱即可。

用法:

raise [SomeException [,arg[,traceback]]

  • SomeException是引發異常的名字,它必須是一個異常類,或異常類的執行個體;
  • args(可選參數)傳遞給SomeException的參數,可以是元組或者是一個單獨的對象。(因為異常參數隻能為元組,是以當args是一個單獨的對象,傳入時會生成一個隻有一個元素的元組)
  • traceback(可選參數),它是當異常觸發時新生産成一個用于異常-正常化的跟蹤記錄對象。
raise Exception
           
---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-5-fca2ab0ca76b> in <module>()
----> 1 raise Exception


Exception: 
           
raise NameError    # 觸發類異常
           
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-10-0350581f77f1> in <module>()
----> 1 raise NameError    # 觸發類異常


NameError: 
           
raise NameError() # 觸發類的執行個體異常
           
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-9-1ca845d32a12> in <module>()
----> 1 raise NameError() # 觸發類的執行個體異常


NameError: 
           
raise NameError("This is NameError Exception")
           
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-16-7324116b3e3f> in <module>()
----> 1 raise NameError("This is NameError Exception")


NameError: This is NameError Exception
           
raise Exception(NameError("This is NameError Exception"))
           
---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-18-da4ad2b05f15> in <module>()
----> 1 raise Exception(NameError("This is NameError Exception"))


Exception: This is NameError Exception
           

自定義異常類

  • 1)繼承Exception類
  • 2)使用raise語句通過人工的方式觸發

示例:

class CustomError(Exception):
    pass

# raise觸發
raise CustomError
           
---------------------------------------------------------------------------

CustomError                               Traceback (most recent call last)

<ipython-input-19-324753841193> in <module>()
      3 
      4 # raise觸發
----> 5 raise CustomError


CustomError: 
           
class MyError(Exception):
    def __init__(self,value):
        super(MyError,self).__init__(value)
        self.value=value
        

raise MyError("Error Information")
           
---------------------------------------------------------------------------

MyError                                   Traceback (most recent call last)

<ipython-input-21-e7eca55e3136> in <module>()
      5 
      6 
----> 7 raise MyError("Error Information")


MyError: Error Information
           

捕獲異常

目的:使程式能夠不崩潰且正确運作

try-except 語句

  • 将可能出現異常的語句放到try子句;
  • 将處理異常的語句放到except子句中。
try:
    
    tryblock
except Exception:
    exception block
           

工作方式:

  • 首先執行try子句。若沒有發生任何異常,except子句會在try子句執行完後被忽略。
  • 若try子句引發異常,則try子句引發的這個異常的代碼會被忽略掉。
    • 若引發的異常與except中指定的異常的類型相比對,則會跳到except子句中執行except子句;
    • 若引發的異常在except子句中沒有與之相比對的分支,它會傳遞到上一級的try子句中;
    • 若仍然找不到與之比對的except子句,它就會成為一個未處理異常,此時程式會終止運作,并且提示異常資訊。
  • except子句可以沒有接任何異常和異常參數,這時try語句捕獲的任何異常都會交給except子句來處理。
try:
    1/0
except ZeroDivisionError:
    print('The divisor cannot be zero')
           
The divisor cannot be zero
           
def this_fails():
        x = 1/0

try:
    this_fails()
except ZeroDivisionError as err:
    print('Handling run-time error:', err)
           
Handling run-time error: division by zero
           

try-except-else 結構

  • 若try範圍内捕獲了except語句指定的異常,就跳到except語句中繼續執行;
  • 若try範圍内沒有捕獲任何異常,就執行else語句。
try:
    myfile=open('myfile.txt','w')
except:
    print('myfile open failed')
else:
    print('myfile open successfully')
    myfile.close()
           
myfile open successfully
           
class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message
           

finally語句

finally語句是指無論是否引發異常,是否被捕獲都一定會執行的代碼塊。

try-finally語句

這種組合其實無法捕捉異常

  • 若沒有發生異常,則先運作try子句,然後運作finally子句;
  • 若發生異常,則執行finally子句,然後把異常遞交給上層try,控制流不會通過整個try語句。

類似的還有:

try-except-finally語句

## try-except-else-finally語句

finally語句的特性:

  • 當在try語句中含有return語句時,執行到return并不會直接傳回,而時在執行finally語句後再執行return語句;
  • 有時在處理玩finally中的資源釋放之後就不需要繼續處理抛出的異常了,這這種情況下可以考慮在finally語句塊中使用return語句。

處理異常的特殊方法

assert語句

若斷言成功則不采取任何措施,否則會觸發AssertionError的異常。

assert 1==2,'one does not equal two!'
           
---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

<ipython-input-39-3ffa52fc9c11> in <module>()
----> 1 assert 1==2,'one does not equal two!'


AssertionError: one does not equal two!
           

with語句(上下文管理語句)

with語句的目的在于從流程圖中把try,except和finally關鍵字和資源配置設定釋放相關代碼統統去掉,而不像try-except-finally那樣僅僅簡化代碼使之易用。

  • 基本用法:
with context_expr[as var]:
    withblock
           
  • 操作檔案對象:
with open(r'somefileName') as somefile:
    for line in somefile:
        print(line)
        ...
           

使用with語句,不管在處理檔案過程是否發生異常,都能保證with語句執行完後已經關閉了打開的檔案句柄。

with語句是一個控制流語句,它引用了一個上下文管理協定,實作方法是為一個類定義

__enter__

__exit__

兩個函數。每次使用with語句,會首先執行

__enter__

函數,它的傳回值指派給var,當withblock執行完後,會執行

__exit__

函數。

with語句等價與于

try:
    執行__enter__函數
    執行withblock
    
finally:
    執行__exit__
           

探尋有趣之事!