雖然在前面的學習中,已經遇到了錯誤和異常問題,但是一直沒有很認真的研究它?,F在來近距離觀察錯誤和異常。
Python 中的錯誤之一是語法錯誤(syntax errors),比如:
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
上面那句話因為缺少冒號:
,導致解釋器無法解釋,于是報錯。這個報錯行為是由 Python 的語法分析器完成的,并且檢測到了錯誤所在文件和行號(File "<stdin>", line 1
),還以向上箭頭^
標識錯誤位置(后面缺少:
),最后顯示錯誤類型。
錯誤之二是在沒有語法錯誤之后,會出現邏輯錯誤。邏輯錯誤可能會由于不完整或者不合法的輸入導致,也可能是無法生成、計算等,或者是其它邏輯問題。
當 Python 檢測到一個錯誤時,解釋器就無法繼續(xù)執(zhí)行下去,于是拋出異常。
看一個異常(讓 0 做分母了,這是小學生都相信會有異常的):
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
當 Python 拋出異常的時候,首先有“跟蹤記錄(Traceback)”,還可以給它取一個更優(yōu)雅的名字“回溯”。后面顯示異常的詳細信息。異常所在位置(文件、行、在某個模塊)。
最后一行是錯誤類型以及導致異常的原因。
下表中列出常見的異常
異常 | 描述 |
---|---|
NameError | 嘗試訪問一個沒有申明的變量 |
ZeroDivisionError | 除數為 0 |
SyntaxError | 語法錯誤 |
IndexError | 索引超出序列范圍 |
KeyError | 請求一個不存在的字典關鍵字 |
IOError | 輸入輸出錯誤(比如你要讀的文件不存在) |
AttributeError | 嘗試訪問未知的對象屬性 |
為了能夠深入理解,依次舉例,展示異常的出現條件和結果。
>>> bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined
Python 中變量需要初始化,即要賦值。雖然不需要像某些語言那樣聲明,但是要賦值先。因為變量相當于一個標簽,要把它貼到對象上才有意義。
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
貌似這樣簡單的錯誤時不會出現的,但在實際情境中,可能沒有這么容易識別,所以,依然要小心為妙。
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
這種錯誤發(fā)生在 Python 代碼編譯的時候,當編譯到這一句時,解釋器不能講代碼轉化為 Python 字節(jié)碼,就報錯。只有改正才能繼續(xù)。所以,它是在程序運行之前就會出現的(如果有錯)?,F在有不少編輯器都有語法校驗功能,在你寫代碼的時候就能顯示出語法的正誤,這多少會對編程者有幫助。
>>> 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'
這兩個都屬于“雞蛋里面挑骨頭”類型,一定得報錯了。不過在編程實踐中,特別是循環(huán)的時候,常常由于循環(huán)條件設置不合理出現這種類型的錯誤。
>>> f = open("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'foo'
如果你確認有文件,就一定要把路徑寫正確,因為你并沒有告訴 Python 對你的 computer 進行全身搜索,所以,Python 會按照你指定位置去找,找不到就異常。
>>> 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'
屬性不存在。這種錯誤前面多次見到。
其實,Python 內建的異常也不僅僅上面幾個,上面只是列出常見的異常中的幾個。比如還有:
>>> range("aaa")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got str.
總之,如果讀者在調試程序的時候遇到了異常,不要慌張,這是好事情,是 Python 在幫助你修改錯誤。只要認真閱讀異常信息,再用 dir()
,help()
或者官方網站文檔、google 等來協(xié)助,一定能解決問題。
在一段程序中,為了能夠讓程序健壯,必須要處理異常。舉例:
#!/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
運行這段程序,顯示如下過程:
$ 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
$
從運行情況看,當在第二個數,即除數為 0 時,程序并沒有因為這個錯誤而停止,而是給用戶一個友好的提示,讓用戶有機會改正錯誤。這完全得益于程序中“處理異?!钡脑O置,如果沒有“處理異?!?,異常出現,就會導致程序終止。
處理異常的方式之一,使用 try...except...
。
對于上述程序,只看 try 和 except 部分,如果沒有異常發(fā)生,except 子句在 try 語句執(zhí)行之后被忽略;如果 try 子句中有異???,該部分的其它語句被忽略,直接跳到 except 部分,執(zhí)行其后面指定的異常類型及其子句。
except 后面也可以沒有任何異常類型,即無異常參數。如果這樣,不論 try 部分發(fā)生什么異常,都會執(zhí)行 except。
在 except 子句中,可以根據異?;蛘邉e的需要,進行更多的操作。比如:
#!/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
在這里,應用了一個函數 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 子句中,有一個 raise
,作為單獨一個語句。它的含義是將異常信息拋出。并且,except 子句用了一個判斷語句,根據不同的情況確定走不同分支。
if __name__ == "__main__":
c = Calculator()
print c.calc("8/0")
這時候 is_raise = False
,則會:
$ 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
的值改為 True,就是這樣了:
if __name__ == "__main__":
c = Calculator()
c.is_raise = True #通過實例屬性修改
print c.calc("8/0")
運行結果:
$ python 21602.py
zero can not be division.
None
最后的 None 是 c.calc("8/0")
的返回值,因為有 print c.calc("8/0")
,所以被打印出來。
總目錄 | 上節(jié):生成器 | 下節(jié):錯誤和異常(2)
如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。