文章目錄
- 異常的種類
- 異常處理
- 觸發異常
- 使用異常避免崩潰
- else 代碼塊
- 拋出異常
- 處理 ZeroDivisionError 異常
- 處理 FileNotFoundError 異常
- 斷言
異常的種類
在python中不同的異常可以用不同的類型(python中統一了類與類型,類型即類)去標識,一個異常標識一種錯誤
常用異常
- AttributeError 試圖訪問一個對象沒有的屬性,比如foo.x,但是foo沒有屬性x
- IOError 輸入/輸出異常;基本上是無法打開文件
- ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
- IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
- IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
- KeyError 試圖訪問字典里不存在的鍵
- KeyboardInterrupt Ctrl+C被按下
- NameError 使用一個還未被賦予對象的變量
- SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
- TypeError 傳入對象類型與要求的不符合
- UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由于另有一個同名的全局變量,導致你以為正在訪問它
- ValueError 傳入一個調用者不期望的值,即使值的類型是正確的
異常處理
如果錯誤發生的條件是可預知的,我們需要用if進行處理:在錯誤發生之前進行預防
AGE
=
10
while
True
:
age
=
input
(
'>>: '
)
.
strip
(
)
if
age
.
isdigit
(
)
:
#只有在age為字符串形式的整數時,下列代碼才不會出錯,該條件是可預知的
age
=
int
(
age
)
if
age
==
AGE
:
print
(
'you got it'
)
break
如果錯誤發生的條件是不可預知的,則需要用到
try...except
:在錯誤發生之后進行處理 使用 try-except 代碼塊
當你認為可能發生了錯誤時,可編寫一個try-except代碼塊來處理可能引發的異常。你讓Python嘗試運行一些代碼,并告訴它如果這些代碼引發了指定的異常,該怎么辦。
處理ZeroDivisionError異常的
try-except
代碼塊類似于下面這樣:
try
:
print
(
5
/
0
)
except
ZeroDivisionError
:
print
(
"You can't divide by zero!"
)
將導致錯誤的代碼行print(5/0)放在了一個try代碼塊中。如果try代碼塊中的代碼運行起來沒有問題,Python將跳過except代碼塊;如果try代碼塊中的代碼導致了錯誤, Python將查找這樣的except代碼塊,并運行其中的代碼,即其中指定的錯誤與引發的錯誤相同。
try代碼塊中的代碼引發了ZeroDivisionError異常,因此Python指出了該如何解決問題的except代碼塊,并運行其中的代碼。這樣,用戶看到的是一條友好的錯誤消息,而不是traceback:
You can't divide by zero!
如果try-except代碼塊后面還有其他代碼,程序將接著運行,因為已經告訴了Python如何處理這種錯誤。
- 異常類只能用來處理指定的異常情況,如果非指定異常則無法處理。
s1
=
'hello'
try
:
int
(
s1
)
except
IndexError
as
e
:
# 未捕獲到異常,程序直接報錯
print
e
- 多分支
s1
=
'hello'
try
:
int
(
s1
)
except
IndexError
as
e
:
print
(
e
)
except
KeyError
as
e
:
print
(
e
)
except
ValueError
as
e
:
print
(
e
)
- 萬能異常Exception
s1
=
'hello'
try
:
int
(
s1
)
except
Exception
as
e
:
print
(
e
)
#4 多分支異常與萬能異常
#4.1 如果你想要的效果是,無論出現什么異常,我們統一丟棄,或者使用同一段代碼邏輯去處理他們,那么騷年,大膽的去做吧,只有一個Exception就足夠了。
#4.2 如果你想要的效果是,對于不同的異常我們需要定制不同的處理邏輯,那就需要用到多分支了。
#5 也可以在多分支后來一個Exception
s1
=
'hello'
try
:
int
(
s1
)
except
IndexError
as
e
:
print
(
e
)
except
KeyError
as
e
:
print
(
e
)
except
ValueError
as
e
:
print
(
e
)
except
Exception
as
e
:
print
(
e
)
- 異常的其他結構
s1
=
'hello'
try
:
int
(
s1
)
except
IndexError
as
e
:
print
(
e
)
except
KeyError
as
e
:
print
(
e
)
except
ValueError
as
e
:
print
(
e
)
#except Exception as e:
# print(e)
else
:
print
(
'try內代碼塊沒有異常則執行我'
)
finally
:
print
(
'無論異常與否,都會執行該模塊,通常是進行清理工作'
)
-
**
主動觸發異常
**
try
:
raise
TypeError
(
'類型錯誤'
)
except
Exception
as
e
:
print
(
e
)
- 自定義異常
class
EgonException
(
BaseException
)
:
def
__init__
(
self
,
msg
)
:
self
.
msg
=
msg
def
__str__
(
self
)
:
return
self
.
msg
try
:
raise
EgonException
(
'類型錯誤'
)
except
EgonException
as
e
:
print
(
e
)
#9 斷言:assert 條件
assert
1
==
1
assert
1
==
2
#10 總結try..except
1
:把錯誤處理和真正的工作分開來
2
:代碼更易組織,更清晰,復雜的工作任務更容易實現;
3
:毫無疑問,更安全了,不至于由于一些小的疏忽而使程序意外崩潰了;
觸發異常
我們可以使用raise語句自己觸發異常
raise語法
格式如下:
raise [Exception [, args [, traceback]]]
語句中 Exception 是異常的類型(例如,NameError)參數標準異常中任一種,args 是自已提供的異常參數。
最后一個參數是可選的(在實踐中很少使用),如果存在,是跟蹤異常對象。
一個異常可以是一個字符串,類或對象。 Python的內核提供的異常,大多數都是實例化的類,這是一個類的實例的參數。定義一個異常非常簡單,如下所示:
def
functionName
(
level
)
:
if
level
<
1
:
raise
Exception
(
"Invalid level!"
,
level
)
# 觸發異常后,后面的代碼就不會再執行
注意:為了能夠捕獲異常,"except"語句必須有用相同的異常來拋出類對象或者字符串。
例如我們捕獲以上異常,"except"語句如下所示:
try
:
正常邏輯
except
Exception
,
err
:
觸發自定義異常
else
:
其余代碼
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定義函數
def
mye
(
level
)
:
if
level
<
1
:
raise
Exception
,
"Invalid level!"
# 觸發異常后,后面的代碼就不會再執行
try
:
mye
(
0
)
# 觸發異常
except
Exception
as
err
:
print
1
,
err
else
:
print
2
#打印
1
Invalid level!
使用異常避免崩潰
發生錯誤時,如果程序還有工作沒有完成,妥善地處理錯誤就尤其重要。這種情況經常會出現在要求用戶提供輸入的程序中;如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入,而不至于崩潰。
print
(
"Give me two numbers, and I'll divide them."
)
print
(
"Enter 'q' to quit."
)
while
True
:
first_number
=
input
(
"\nFirst number: "
)
if
first_number
==
'q'
:
break
second_number
=
input
(
"Second number: "
)
if
second_number
==
'q'
:
break
answer
=
int
(
first_number
)
/
int
(
second_number
)
print
(
answer
)
這個程序提示用戶輸入一個數字,并將其存儲到變量first_number中;如果用戶輸入的不是表示退出的q,就再提示用戶輸入一個數字,并將其存儲到變量second_number中,接下來,我們計算這兩個數字的商。這個程序沒有采取任何處理錯誤的措施,因此讓它執行除數為0的除法運算時,它將崩潰:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
Traceback (most recent call last):
File "division.py", line 9, in
answer = int(first_number) / int(second_number)
ZeroDivisionError: division by zero
else 代碼塊
通過將可能引發錯誤的代碼放在
try-except
代碼塊中,可提高這個程序抵御錯誤的能力。錯誤是執行除法運算的代碼行導致的,因此我們需要將它放到
try-except
代碼塊中。這個示例還包含一個else代碼塊;依賴于try代碼塊成功執行的代碼都應放到else代碼塊中:
print
(
"Give me two numbers, and I'll divide them."
)
print
(
"Enter 'q' to quit."
)
while
True
:
first_number
=
input
(
"\nFirst number: "
)
if
first_number
==
'q'
:
break
second_number
=
input
(
"Second number: "
)
try
:
answer
=
int
(
first_number
)
/
int
(
second_number
)
except
ZeroDivisionError
:
print
(
"You can't divide by 0!"
)
else
:
print
(
answer
)
讓Python嘗試執行try代碼塊中的除法運算,這個代碼塊只包含可能導致錯誤的代碼。依賴于try代碼塊成功執行的代碼都放在else代碼塊中;在這個示例中,如果除法運算成功,就使用else代碼塊來打印結果。
except代碼塊告訴Python,出現ZeroDivisionError異常時該怎么辦。如果try代碼塊因除零錯誤而失敗,我們就打印一條友好的消息,告訴用戶如何避免這種錯誤。程序將繼續運行,用戶根本看不到traceback:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
You can't divide by 0!
First number: 5
Second number: 2
2.5
First number: q
try-except-else代碼塊的工作原理大致如下: Python嘗試執行try代碼塊中的代碼;只有可能引發異常的代碼才需要放在try語句中。有時候,有一些僅在try代碼塊成功執行時才需要運行的代碼;這些代碼應放在else代碼塊中。 except代碼塊告訴Python,如果它嘗試運行try代碼塊中的代碼時引發了指定的異常,該怎么辦。
通過預測可能發生錯誤的代碼,可編寫健壯的程序,它們即便面臨無效數據或缺少資源,也能繼續運行,從而能夠抵御無意的用戶錯誤和惡意的攻擊。
拋出異常
已看到如何使用 try 和 except 語句來處理 Python 的異常,這樣程序就可以從你預期的異常中恢復。
但你也可以在代碼中拋出自己的異常。拋出異常相當于是說:“停止運行這個函數中的代碼,將程序執行轉到 except 語句 ”
拋出異常使用 raise 語句。在代碼中, raise 語句包含以下部分:
- raise 關鍵字;
- 對 Exception 函數的調用;
- 傳遞給 Exception 函數的字符串,包含有用的出錯信息。
>>
>
raise
Exception
(
'This is the error message.'
)
Traceback
(
most recent call last
)
:
File
"
"
,
line
1
,
in
<
module
>
raise
Exception
(
'This is the error message.'
)
Exception
:
This
is
the error message
.
如果沒有 try 和 except 語句覆蓋拋出異常的 raise 語句,該程序就會崩潰,并顯示異常的出錯信息。
通常是調用該函數的代碼知道如何處理異常,而不是該函數本身。所以你常常會看到 raise 語句在一個函數中, try 和 except 語句在調用該函數的代碼中。 例如,打開一個新的文件編輯器窗口,輸入以下代碼,并保存為 boxPrint.py:
def
boxPrint
(
symbol
,
width
,
height
)
:
if
len
(
symbol
)
!=
1
:
raise
Exception
(
'Symbol must be a single character string.'
)
if
width
<=
2
:
raise
Exception
(
'Width must be greater than 2.'
)
if
height
<=
2
:
raise
Exception
(
'Height must be greater than 2.'
)
print
(
symbol
*
width
)
for
i
in
range
(
height
-
2
)
:
print
(
symbol
+
(
' '
*
(
width
-
2
)
)
+
symbol
)
print
(
symbol
*
width
)
for
sym
,
w
,
h
in
(
(
'*'
,
4
,
4
)
,
(
'O'
,
20
,
5
)
,
(
'x'
,
1
,
3
)
,
(
'ZZ'
,
3
,
3
)
)
:
try
:
boxPrint
(
sym
,
w
,
h
)
except
Exception
as
err
:
print
(
'An exception happened: '
+
str
(
err
)
)
處理 ZeroDivisionError 異常
下面來看一種導致Python引發異常的簡單錯誤。你可能知道不能將一個數字除以0,但我們還是讓Python這樣做吧:
print
(
5
/
0
)
顯然, Python無法這樣做,因此你將看到一個traceback:
Traceback
(
most recent call last
)
:
File
"division.py"
,
line
1
,
in
<
module
>
print
(
5
/
0
)
ZeroDivisionError
:
division by zero
在上述traceback中, 錯誤
ZeroDivisionError
是一個異常對象。 Python無法按你的要求做時,就會創建這種對象。在這種情況下, Python將停止運行程序,并指出引發了哪種異常,而我們可根據這些信息對程序進行修改。下面我們將告訴Python,發生這種錯誤時怎么辦;這樣,如果再次發生這樣的錯誤,我們就有備無患了。
處理 FileNotFoundError 異常
使用文件時,一種常見的問題是找不到文件:你要查找的文件可能在其他地方、文件名可能不正確或者這個文件根本就不存在。對于所有這些情形,都可使用try-except代碼塊以直觀的方式進行處理。
嘗試讀取一個不存在的文件。下面的程序嘗試讀取文件alice.txt的內容,但我沒有將這個文件存儲在alice.py所在的目錄中:
#alice.py
filename
=
'alice.txt'
with
open
(
filename
)
as
f_obj
:
contents
=
f_obj
.
read
(
)
Python無法讀取不存在的文件,因此它引發一個異常:
Traceback
(
most recent call last
)
:
File
"alice.py"
,
line
3
,
in
<
module
>
with
open
(
filename
)
as
f_obj
:
FileNotFoundError
:
[
Errno
2
]
No such
file
or
directory
:
'alice.txt'
在上述traceback中,最后一行報告了FileNotFoundError異常,這是Python找不到要打開的文件時創建的異常。在這個示例中,這個
錯誤是函數open()導致的,
因此要處理這個錯誤,必須
將try語句放在包含open()的代碼行之前
:
filename
=
'alice.txt'
try
:
with
open
(
filename
)
as
f_obj
:
contents
=
f_obj
.
read
(
)
except
FileNotFoundError
:
msg
=
"Sorry, the file "
+
filename
+
" does not exist."
print
(
msg
)
在這個示例中, try代碼塊引發FileNotFoundError異常,因此Python找出與該錯誤匹配的except代碼塊,并運行其中的代碼。最終的結果是顯示一條友好的錯誤消息,而不是traceback:
Sorry, the file alice.txt does not exist.
斷言
????????“斷言”是一個心智正常的檢查,確保代碼沒有做什么明顯錯誤的事情。這些心智正常的檢查由
assert 語句
執行。如果檢查失敗,就會拋出異常。在代碼中, assert語句包含以下部分:
- assert 關鍵字;
- 條件(即求值為 True 或 False 的表達式);
- 逗號;
- 當條件為 False 時顯示的字符串。
>>
>
podBayDoorStatus
=
'open'
>>
>
assert
podBayDoorStatus
==
'open'
,
'The pod bay doors need to be "open".'
>>
>
podBayDoorStatus
=
'I\'m sorry, Dave. I\'m afraid I can'
t do that
.
''
>>
>
assert
podBayDoorStatus
==
'open'
,
'The pod bay doors need to be "open".'
Traceback
(
most recent call last
)
:
File
"
"
,
line
1
,
in
<
module
>
assert
podBayDoorStatus
==
'open'
,
'The pod bay doors need to be "open".'
AssertionError
:
The pod bay doors need to be
"open"
.
這里將 podBayDoorStatus 設置為 ‘open’,所以從此以后,我們充分期望這個變量的值是 ‘open’。在使用這個變量的程序中,基于這個值是 ‘open’ 的假定,我們可能寫下了大量的代碼,即這些代碼依賴于它是 ‘open’,才能按照期望工作。所以添加了一個斷言,確保假定 podBayDoorStatus 是 ‘open’ 是對的。這里,我們加入了信息 ‘Thepod bay doors need to be “open”.’,這樣如果斷言失敗,就很容易看到哪里出了錯。
稍后,假如我們犯了一個明顯的錯誤,把另外的值賦給 podBayDoorStatus,但在很多行代碼中,我們并沒有意識到這一點。這個斷言會抓住這個錯誤,清楚地告訴我們出了什么錯。
在日常英語中, assert 語句是說:“我斷言這個條件為真,如果不為真,程序中什么地方就有一個缺陷。”不像異常,代碼不應該用 try 和 except 處理 assert 語句。如果assert 失敗,程序就應該崩潰。通過這樣的快速失敗,產生缺陷和你第一次注意到該缺陷之間的時間就縮短了。這將減少為了尋找導致該缺陷的代碼,而需要檢查的代碼量。
斷言針對的是程序員的錯誤,而不是用戶的錯誤。對于那些可以恢復的錯誤(諸如文件沒有找到,或用戶輸入了無效的數據),請拋出異常,而不是用 assert 語句檢測它。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
