錯(cuò)誤和異常(1)

2018-02-24 15:48 更新

雖然在前面的學(xué)習(xí)中,已經(jīng)遇到了錯(cuò)誤和異常問(wèn)題,但是一直沒(méi)有很認(rèn)真的研究它。現(xiàn)在來(lái)近距離觀察錯(cuò)誤和異常。

錯(cuò)誤

python中的錯(cuò)誤之一是語(yǔ)法錯(cuò)誤(syntax errors),比如:

>>> for i in range(10)
  File "<stdin>", line 1
    for i in range(10)
                     ^
SyntaxError: invalid syntax

上面那句話因?yàn)槿鄙倜疤?hào):,導(dǎo)致解釋器無(wú)法解釋?zhuān)谑菆?bào)錯(cuò)。這個(gè)報(bào)錯(cuò)行為是由python的語(yǔ)法分析器完成的,并且檢測(cè)到了錯(cuò)誤所在文件和行號(hào)(File "<stdin>", line 1),還以向上箭頭^標(biāo)識(shí)錯(cuò)誤位置(后面缺少:),最后顯示錯(cuò)誤類(lèi)型。

錯(cuò)誤之二是在沒(méi)有語(yǔ)法錯(cuò)誤之后,會(huì)出現(xiàn)邏輯錯(cuò)誤。邏輯錯(cuò)誤可能會(huì)由于不完整或者不合法的輸入導(dǎo)致,也可能是無(wú)法生成、計(jì)算等,或者是其它邏輯問(wèn)題。

當(dāng)python檢測(cè)到一個(gè)錯(cuò)誤時(shí),解釋器就無(wú)法繼續(xù)執(zhí)行下去,于是拋出異常。

異常

看一個(gè)異常(讓0做分母了,這是小學(xué)生都相信會(huì)有異常的):

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

當(dāng)python拋出異常的時(shí)候,首先有“跟蹤記錄(Traceback)”,還可以給它取一個(gè)更優(yōu)雅的名字“回溯”。后面顯示異常的詳細(xì)信息。異常所在位置(文件、行、在某個(gè)模塊)。

最后一行是錯(cuò)誤類(lèi)型以及導(dǎo)致異常的原因。

下表中列出常見(jiàn)的異常

異常 描述
NameError 嘗試訪問(wèn)一個(gè)沒(méi)有申明的變量
ZeroDivisionError 除數(shù)為0
SyntaxError 語(yǔ)法錯(cuò)誤
IndexError 索引超出序列范圍
KeyError 請(qǐng)求一個(gè)不存在的字典關(guān)鍵字
IOError 輸入輸出錯(cuò)誤(比如你要讀的文件不存在)
AttributeError 嘗試訪問(wèn)未知的對(duì)象屬性

為了能夠深入理解,依次舉例,展示異常的出現(xiàn)條件和結(jié)果。

NameError

>>> bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined

python中變量需要初始化,即要賦值。雖然不需要像某些語(yǔ)言那樣聲明,但是要賦值先。因?yàn)樽兞肯喈?dāng)于一個(gè)標(biāo)簽,要把它貼到對(duì)象上才有意義。

ZeroDivisionError

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

貌似這樣簡(jiǎn)單的錯(cuò)誤時(shí)不會(huì)出現(xiàn)的,但在實(shí)際情境中,可能沒(méi)有這么容易識(shí)別,所以,依然要小心為妙。

SyntaxError

>>> for i in range(10)
  File "<stdin>", line 1
    for i in range(10)
                     ^
SyntaxError: invalid syntax

這種錯(cuò)誤發(fā)生在python代碼編譯的時(shí)候,當(dāng)編譯到這一句時(shí),解釋器不能講代碼轉(zhuǎn)化為python字節(jié)碼,就報(bào)錯(cuò)。只有改正才能繼續(xù)。所以,它是在程序運(yùn)行之前就會(huì)出現(xiàn)的(如果有錯(cuò))?,F(xiàn)在有不少編輯器都有語(yǔ)法校驗(yàn)功能,在你寫(xiě)代碼的時(shí)候就能顯示出語(yǔ)法的正誤,這多少會(huì)對(duì)編程者有幫助。

IndexError

>>> a = [1,2,3]
>>> a[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

>>> d = {"python":"itdiffer.com"}
>>> d["java"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'java'

這兩個(gè)都屬于“雞蛋里面挑骨頭”類(lèi)型,一定得報(bào)錯(cuò)了。不過(guò)在編程實(shí)踐中,特別是循環(huán)的時(shí)候,常常由于循環(huán)條件設(shè)置不合理出現(xiàn)這種類(lèi)型的錯(cuò)誤。

IOError

>>> f = open("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'foo'

如果你確認(rèn)有文件,就一定要把路徑寫(xiě)正確,因?yàn)槟悴](méi)有告訴python對(duì)你的computer進(jìn)行全身搜索,所以,python會(huì)按照你指定位置去找,找不到就異常。

AttributeError

>>> class A(object): pass
... 
>>> a = A()
>>> a.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'

屬性不存在。這種錯(cuò)誤前面多次見(jiàn)到。

其實(shí),python內(nèi)建的異常也不僅僅上面幾個(gè),上面只是列出常見(jiàn)的異常中的幾個(gè)。比如還有:

>>> range("aaa")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got str.

總之,如果讀者在調(diào)試程序的時(shí)候遇到了異常,不要慌張,這是好事情,是python在幫助你修改錯(cuò)誤。只要認(rèn)真閱讀異常信息,再用dir(),help()或者官方網(wǎng)站文檔、google等來(lái)協(xié)助,一定能解決問(wèn)題。

處理異常

在一段程序中,為了能夠讓程序健壯,必須要處理異常。舉例:

#!/usr/bin/env python
# coding=utf-8

while 1:
    print "this is a division program."
    c = raw_input("input 'c' continue, otherwise logout:")
    if c == 'c':
        a = raw_input("first number:")
        b = raw_input("second number:")
        try:
            print float(a)/float(b)
            print "*************************"
        except ZeroDivisionError:
            print "The second number can't be zero!"
            print "*************************"
    else:
        break

運(yùn)行這段程序,顯示如下過(guò)程:

$ python 21601.py 
this is a division program.
input 'c' continue, otherwise logout:c
first number:5
second number:2
2.5
*************************
this is a division program.
input 'c' continue, otherwise logout:c
first number:5
second number:0
The second number can't be zero!
*************************
this is a division program.
input 'c' continue, otherwise logout:d
$

從運(yùn)行情況看,當(dāng)在第二個(gè)數(shù),即除數(shù)為0時(shí),程序并沒(méi)有因?yàn)檫@個(gè)錯(cuò)誤而停止,而是給用戶一個(gè)友好的提示,讓用戶有機(jī)會(huì)改正錯(cuò)誤。這完全得益于程序中“處理異常”的設(shè)置,如果沒(méi)有“處理異?!保惓3霈F(xiàn),就會(huì)導(dǎo)致程序終止。

處理異常的方式之一,使用try...except...。

對(duì)于上述程序,只看try和except部分,如果沒(méi)有異常發(fā)生,except子句在try語(yǔ)句執(zhí)行之后被忽略;如果try子句中有異常可,該部分的其它語(yǔ)句被忽略,直接跳到except部分,執(zhí)行其后面指定的異常類(lèi)型及其子句。

except后面也可以沒(méi)有任何異常類(lèi)型,即無(wú)異常參數(shù)。如果這樣,不論try部分發(fā)生什么異常,都會(huì)執(zhí)行except。

在except子句中,可以根據(jù)異?;蛘邉e的需要,進(jìn)行更多的操作。比如:

#!/usr/bin/env python
# coding=utf-8

class Calculator(object):
    is_raise = False
    def calc(self, express):
        try:
            return eval(express)
        except ZeroDivisionError:
            if self.is_raise:
                print "zero can not be division."
            else:
                raise

在這里,應(yīng)用了一個(gè)函數(shù)eval(),它的含義是:

eval(...)
    eval(source[, globals[, locals]]) -> value

    Evaluate the source in the context of globals and locals.
    The source may be a string representing a Python expression
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.

例如:

>>> eval("3+5")
8

另外,在except子句中,有一個(gè)raise,作為單獨(dú)一個(gè)語(yǔ)句。它的含義是將異常信息拋出。并且,except子句用了一個(gè)判斷語(yǔ)句,根據(jù)不同的情況確定走不同分支。

if __name__ == "__main__":
    c = Calculator()
    print c.calc("8/0")

這時(shí)候is_raise = False,則會(huì):

$ python 21602.py 
Traceback (most recent call last):
  File "21602.py", line 17, in <module>
    print c.calc("8/0")
  File "21602.py", line 8, in calc
    return eval(express)
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

如果將is_raise的值改為T(mén)rue,就是這樣了:

if __name__ == "__main__":
    c = Calculator()
    c.is_raise = True    #通過(guò)實(shí)例屬性修改
    print c.calc("8/0")

運(yùn)行結(jié)果:

$ python 21602.py 
zero can not be division.
None

最后的None是c.calc("8/0")的返回值,因?yàn)橛?code>print c.calc("8/0"),所以被打印出來(lái)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)