雖然在前面的學(xué)習(xí)中,已經(jīng)遇到了錯(cuò)誤和異常問(wèn)題,但是一直沒(méi)有很認(rèn)真的研究它。現(xiàn)在來(lái)近距離觀察錯(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é)果。
>>> 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ì)象上才有意義。
>>> 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í)別,所以,依然要小心為妙。
>>> 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ì)編程者有幫助。
>>> 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ò)誤。
>>> 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ì)按照你指定位置去找,找不到就異常。
>>> 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)。
更多建議: