天天看點

Python基礎之:Python中的異常和錯誤

文章目錄

  • 簡介
  • Python中的内置異常類
  • 文法錯誤
  • 異常
  • 異常處理
  • 抛出異常
  • 異常鍊
  • 自定義異常
  • finally

和其他的語言一樣,Python中也有異常和錯誤。在 Python 中,所有異常都是

BaseException

的類的執行個體。 今天我們來詳細看一下Python中的異常和對他們的處理方式。

Python中所有異常類都來自BaseException,它是所有内置異常的基類。

雖然它是所有異常類的基類,但是對于使用者自定義的類來說,并不推薦直接繼承BaseException,而是繼承Exception.

先看下Python中異常類的結構關系:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError      |    +-- FloatingPointError      |    +-- OverflowError      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError      |    +-- ModuleNotFoundError
      +-- LookupError      |    +-- IndexError      |    +-- KeyError
      +-- MemoryError
      +-- NameError      |    +-- UnboundLocalError
      +-- OSError      |    +-- BlockingIOError      |    +-- ChildProcessError      |    +-- ConnectionError      |    |    +-- BrokenPipeError      |    |    +-- ConnectionAbortedError      |    |    +-- ConnectionRefusedError      |    |    +-- ConnectionResetError      |    +-- FileExistsError      |    +-- FileNotFoundError      |    +-- InterruptedError      |    +-- IsADirectoryError      |    +-- NotADirectoryError      |    +-- PermissionError      |    +-- ProcessLookupError      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError      |    +-- NotImplementedError      |    +-- RecursionError
      +-- SyntaxError      |    +-- IndentationError      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError      |    +-- UnicodeError      |         +-- UnicodeDecodeError      |         +-- UnicodeEncodeError      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning      

其中BaseException,Exception,ArithmeticError,BufferError,LookupError 主要被作為其他異常的基類。

在Python中,對于異常和錯誤通常可以分為兩類,第一類是文法錯誤,又稱解析錯誤。也就是代碼還沒有開始運作,就發生的錯誤。

其産生的原因就是編寫的代碼不符合Python的語言規範:

>>> while True print('Hello world')
  File "<stdin>", line 1while True print('Hello world')   ^SyntaxError: invalid syntax      

上面代碼原因是 print 前面少了 冒号。

即使我們的程式符合python的文法規範,但是在執行的時候,仍然可能發送錯誤,這種在運作時發送的錯誤,叫做異常。

看一下下面的異常:

>>> 10 * (1/0)Traceback (most recent call last):
  File "<stdin>", line 1, in <module>ZeroDivisionError: division by zero>>> 4 + spam*3Traceback (most recent call last):
  File "<stdin>", line 1, in <module>NameError: name 'spam' is not defined>>> '2' + 2Traceback (most recent call last):
  File "<stdin>", line 1, in <module>TypeError: Can't convert 'int' object to str implicitly      

程式發生了異常之後該怎麼處理呢?

我們可以使用try except 語句來捕獲特定的異常。

>>> while True:...     try:...         x = int(input("Please enter a number: "))...         break...     except ValueError:...         print("Oops!  That was no valid number.  Try again...")...      

上面代碼的執行流程是,首先執行try中的子語句,如果沒有異常發生,那麼就會跳過except,并完成try語句的執行。

如果try中的子語句中發生了異常,那麼将會跳過try子句中的後面部分,進行except的異常比對。如果比對成功的話,就會去執行except中的子語句。

如果發生的異常和 except 子句中指定的異常不比對,則将其傳遞到外部的

try

語句中。

一個try中可以有多個except 子句,我們可以這樣寫:

    try:raise cls()except D:print("D")except C:print("C")except B:print("B")      

一個except也可以帶多個異常:

... except (RuntimeError, TypeError, NameError):...     pass      

except 子句還可以省略異常名,用來比對所有的異常:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
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 OSError:print('cannot open', arg)else:print(arg, 'has', len(f.readlines()), 'lines')f.close()      

except可以指定異常變量的名字

instance

,這個變量代表這個異常執行個體。

我們可以通過instance.args來輸出異常的參數。

同時,因為異常執行個體定義了

__str__()

,是以可以直接使用print來輸出異常的參數。而不需要使用

.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,...                          # but may be overridden in exception subclasses...     x, y = inst.args     # unpack args...     print('x =', x)...     print('y =', y)...<class 'Exception'>('spam', 'eggs')('spam', 'eggs')x = spam
y = eggs      

上面的例子中,我們在try字句中抛出了一個異常,并且指定了2個參數。

我們可以使用raise語句來抛出異常。

>>> raise NameError('HiThere')Traceback (most recent call last):
  File "<stdin>", line 1, in <module>NameError: HiThere      

raise的參數是一個異常,這個異常可以是異常執行個體或者是一個異常類。

注意,這個異常類必須是

Exception

的子類。

如果傳遞的是一個異常類,那麼将會調用無參構造函數來隐式執行個體化:

raise ValueError  # shorthand for 'raise ValueError()'      

如果我們捕獲了某些異常,但是又不想去處理,那麼可以在except語句中使用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 <module>NameError: HiThere      

如果我們通過except捕獲一個異常A之後,可以通過raise語句再次抛出一個不同的異常類型B。

那麼我們看到的這個異常資訊就是B的資訊。但是我們并不知道這個異常B是從哪裡來的,這時候,我們就可以用到異常鍊。

異常鍊就是抛出異常的時候,使用raise from語句:

>>> def func():...     raise IOError...>>> try:...     func()... except IOError as exc:...     raise RuntimeError('Failed to open database') from exc...Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in func
OSError

The above exception was the direct cause of the following exception:Traceback (most recent call last):
  File "<stdin>", line 4, in <module>RuntimeError: Failed to open database      

上面的例子中,我們在捕獲IOError之後,又抛出了RuntimeError,通過使用異常鍊,我們很清晰的看出這兩個異常之間的關系。

預設情況下,如果異常是從except 或者 finally 中抛出的話,會自動帶上異常鍊資訊。

如果你不想帶上異常鍊,那麼可以

from None

try:open('database.sqlite')except IOError:raise RuntimeError from NoneTraceback (most recent call last):
  File "<stdin>", line 4, in <module>RuntimeError      

使用者可以繼承 Exception 來實作自定義的異常,我們看一些自定義異常的例子:

class Error(Exception):"""Base class for exceptions in this module."""passclass 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 = messageclass 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 = nextself.message = message      

try語句可以跟着一個finally語句來實作一些收尾操作。

>>> try:...     raise KeyboardInterrupt... finally:...     print('Goodbye, world!')...Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>      

finally

子句将作為

try

語句結束前的最後一項任務被執行, 無論try中是否産生異常,finally語句中的代碼都會被執行。

如果

finally

子句中包含一個

return

語句,則傳回值将來自

finally

子句的某個

return

語句的傳回值,而非來自

try

子句的

return

語句的傳回值。

>>> def bool_return():...     try:...         return True...     finally:...         return False...>>> bool_return()False      

繼續閱讀