雖然在前面的學習中,已經遇到了錯誤和異常問題,但是一直沒有很認真的研究它。現在來近距離觀察錯誤和異常。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#錯誤)錯誤
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檢測到一個錯誤時,解釋器就無法繼續執行下去,于是拋出異常。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#異常)異常
看一個異常(讓0做分母了,這是小學生都相信會有異常的):
~~~
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
~~~
當python拋出異常的時候,首先有“跟蹤記錄(Traceback)”,還可以給它取一個更優雅的名字“回溯”。后面顯示異常的詳細信息。異常所在位置(文件、行、在某個模塊)。
最后一行是錯誤類型以及導致異常的原因。
下表中列出常見的異常
| 異常 | 描述 |
| --- | --- |
| NameError | 嘗試訪問一個沒有申明的變量 |
| ZeroDivisionError | 除數為0 |
| SyntaxError | 語法錯誤 |
| IndexError | 索引超出序列范圍 |
| KeyError | 請求一個不存在的字典關鍵字 |
| IOError | 輸入輸出錯誤(比如你要讀的文件不存在) |
| AttributeError | 嘗試訪問未知的對象屬性 |
為了能夠深入理解,依次舉例,展示異常的出現條件和結果。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#nameerror)NameError
~~~
>>> bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined
~~~
python中變量需要初始化,即要賦值。雖然不需要像某些語言那樣聲明,但是要賦值先。因為變量相當于一個標簽,要把它貼到對象上才有意義。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#zerodivisionerror)ZeroDivisionError
~~~
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
~~~
貌似這樣簡單的錯誤時不會出現的,但在實際情境中,可能沒有這么容易識別,所以,依然要小心為妙。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#syntaxerror)SyntaxError
~~~
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
~~~
這種錯誤發生在python代碼編譯的時候,當編譯到這一句時,解釋器不能講代碼轉化為python字節碼,就報錯。只有改正才能繼續。所以,它是在程序運行之前就會出現的(如果有錯)。現在有不少編輯器都有語法校驗功能,在你寫代碼的時候就能顯示出語法的正誤,這多少會對編程者有幫助。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#indexerror)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'
~~~
這兩個都屬于“雞蛋里面挑骨頭”類型,一定得報錯了。不過在編程實踐中,特別是循環的時候,常常由于循環條件設置不合理出現這種類型的錯誤。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#ioerror)IOError
~~~
>>> 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會按照你指定位置去找,找不到就異常。
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#attributeerror)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'
~~~
屬性不存在。這種錯誤前面多次見到。
其實,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等來協助,一定能解決問題。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/216.md#處理異常)處理異常
在一段程序中,為了能夠讓程序健壯,必須要處理異常。舉例:
~~~
#!/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時,程序并沒有因為這個錯誤而停止,而是給用戶一個友好的提示,讓用戶有機會改正錯誤。這完全得益于程序中“處理異常”的設置,如果沒有“處理異常”,異常出現,就會導致程序終止。
處理異常的方式之一,使用`try...except...`。
對于上述程序,只看try和except部分,如果沒有異常發生,except子句在try語句執行之后被忽略;如果try子句中有異常可,該部分的其它語句被忽略,直接跳到except部分,執行其后面指定的異常類型及其子句。
except后面也可以沒有任何異常類型,即無異常參數。如果這樣,不論try部分發生什么異常,都會執行except。
在except子句中,可以根據異常或者別的需要,進行更多的操作。比如:
~~~
#!/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")`,所以被打印出來。
- 第零章 預備
- 關于Python的故事
- 從小工到專家
- Python安裝
- 集成開發環境
- 第壹章 基本數據類型
- 數和四則運算
- 除法
- 常用數學函數和運算優先級
- 寫一個簡單的程序
- 字符串(1)
- 字符串(2)
- 字符串(3)
- 字符串(4)
- 字符編碼
- 列表(1)
- 列表(2)
- 列表(3)
- 回顧list和str
- 元組
- 字典(1)
- 字典(2)
- 集合(1)
- 集合(2)
- 第貳章 語句和文件
- 運算符
- 語句(1)
- 語句(2)
- 語句(3)
- 語句(4)
- 語句(5)
- 文件(1)
- 文件(2)
- 迭代
- 練習
- 自省
- 第叁章 函數
- 函數(1)
- 函數(2)
- 函數(3)
- 函數(4)
- 函數練習
- 第肆章 類
- 類(1)
- 類(2)
- 類(3)
- 類(4)
- 類(5)
- 多態和封裝
- 特殊方法(1)
- 特殊方法(2)
- 迭代器
- 生成器
- 上下文管理器
- 第伍章 錯誤和異常
- 錯誤和異常(1)
- 錯誤和異常(2)
- 錯誤和異常(3)
- 第陸章 模塊
- 編寫模塊
- 標準庫(1)
- 標準庫(2)
- 標準庫(3)
- 標準庫(4)
- 標準庫(5)
- 標準庫(6)
- 標準庫(7)
- 標準庫(8)
- 第三方庫
- 第柒章 保存數據
- 將數據存入文件
- mysql數據庫(1)
- MySQL數據庫(2)
- mongodb數據庫(1)
- SQLite數據庫
- 電子表格
- 第捌章 用Tornado做網站
- 為做網站而準備
- 分析Hello
- 用tornado做網站(1)
- 用tornado做網站(2)
- 用tornado做網站(3)
- 用tornado做網站(4)
- 用tornado做網站(5)
- 用tornado做網站(6)
- 用tornado做網站(7)
- 第玖章 科學計算
- 為計算做準備
- Pandas使用(1)
- Pandas使用(2)
- 處理股票數據
- 附:網絡文摘
- 如何成為Python高手
- ASCII、Unicode、GBK和UTF-8字符編碼的區別聯系