文章目錄
- 異常的種類
- 異常處理
- 觸發(fā)異常
- 使用異常避免崩潰
- else 代碼塊
- 拋出異常
- 處理 ZeroDivisionError 異常
- 處理 FileNotFoundError 異常
- 斷言
異常的種類
在python中不同的異常可以用不同的類型(python中統(tǒng)一了類與類型,類型即類)去標(biāo)識,一個異常標(biāo)識一種錯誤
常用異常
- AttributeError 試圖訪問一個對象沒有的屬性,比如foo.x,但是foo沒有屬性x
- IOError 輸入/輸出異常;基本上是無法打開文件
- ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
- IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
- IndexError 下標(biāo)索引超出序列邊界,比如當(dāng)x只有三個元素,卻試圖訪問x[5]
- KeyError 試圖訪問字典里不存在的鍵
- KeyboardInterrupt Ctrl+C被按下
- NameError 使用一個還未被賦予對象的變量
- SyntaxError Python代碼非法,代碼不能編譯(個人認(rèn)為這是語法錯誤,寫錯了)
- TypeError 傳入對象類型與要求的不符合
- UnboundLocalError 試圖訪問一個還未被設(shè)置的局部變量,基本上是由于另有一個同名的全局變量,導(dǎo)致你以為正在訪問它
- ValueError 傳入一個調(diào)用者不期望的值,即使值的類型是正確的
異常處理
如果錯誤發(fā)生的條件是可預(yù)知的,我們需要用if進(jìn)行處理:在錯誤發(fā)生之前進(jìn)行預(yù)防
AGE
=
10
while
True
:
age
=
input
(
'>>: '
)
.
strip
(
)
if
age
.
isdigit
(
)
:
#只有在age為字符串形式的整數(shù)時,下列代碼才不會出錯,該條件是可預(yù)知的
age
=
int
(
age
)
if
age
==
AGE
:
print
(
'you got it'
)
break
如果錯誤發(fā)生的條件是不可預(yù)知的,則需要用到
try...except
:在錯誤發(fā)生之后進(jìn)行處理 使用 try-except 代碼塊
當(dāng)你認(rèn)為可能發(fā)生了錯誤時,可編寫一個try-except代碼塊來處理可能引發(fā)的異常。你讓Python嘗試運行一些代碼,并告訴它如果這些代碼引發(fā)了指定的異常,該怎么辦。
處理ZeroDivisionError異常的
try-except
代碼塊類似于下面這樣:
try
:
print
(
5
/
0
)
except
ZeroDivisionError
:
print
(
"You can't divide by zero!"
)
將導(dǎo)致錯誤的代碼行print(5/0)放在了一個try代碼塊中。如果try代碼塊中的代碼運行起來沒有問題,Python將跳過except代碼塊;如果try代碼塊中的代碼導(dǎo)致了錯誤, Python將查找這樣的except代碼塊,并運行其中的代碼,即其中指定的錯誤與引發(fā)的錯誤相同。
try代碼塊中的代碼引發(fā)了ZeroDivisionError異常,因此Python指出了該如何解決問題的except代碼塊,并運行其中的代碼。這樣,用戶看到的是一條友好的錯誤消息,而不是traceback:
You can't divide by zero!
如果try-except代碼塊后面還有其他代碼,程序?qū)⒔又\行,因為已經(jīng)告訴了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 如果你想要的效果是,無論出現(xiàn)什么異常,我們統(tǒng)一丟棄,或者使用同一段代碼邏輯去處理他們,那么騷年,大膽的去做吧,只有一個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
)
- 異常的其他結(jié)構(gòu)
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內(nèi)代碼塊沒有異常則執(zhí)行我'
)
finally
:
print
(
'無論異常與否,都會執(zhí)行該模塊,通常是進(jìn)行清理工作'
)
-
**
主動觸發(fā)異常
**
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 總結(jié)try..except
1
:把錯誤處理和真正的工作分開來
2
:代碼更易組織,更清晰,復(fù)雜的工作任務(wù)更容易實現(xiàn);
3
:毫無疑問,更安全了,不至于由于一些小的疏忽而使程序意外崩潰了;
觸發(fā)異常
我們可以使用raise語句自己觸發(fā)異常
raise語法
格式如下:
raise [Exception [, args [, traceback]]]
語句中 Exception 是異常的類型(例如,NameError)參數(shù)標(biāo)準(zhǔn)異常中任一種,args 是自已提供的異常參數(shù)。
最后一個參數(shù)是可選的(在實踐中很少使用),如果存在,是跟蹤異常對象。
一個異常可以是一個字符串,類或?qū)ο蟆?Python的內(nèi)核提供的異常,大多數(shù)都是實例化的類,這是一個類的實例的參數(shù)。定義一個異常非常簡單,如下所示:
def
functionName
(
level
)
:
if
level
<
1
:
raise
Exception
(
"Invalid level!"
,
level
)
# 觸發(fā)異常后,后面的代碼就不會再執(zhí)行
注意:為了能夠捕獲異常,"except"語句必須有用相同的異常來拋出類對象或者字符串。
例如我們捕獲以上異常,"except"語句如下所示:
try
:
正常邏輯
except
Exception
,
err
:
觸發(fā)自定義異常
else
:
其余代碼
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 定義函數(shù)
def
mye
(
level
)
:
if
level
<
1
:
raise
Exception
,
"Invalid level!"
# 觸發(fā)異常后,后面的代碼就不會再執(zhí)行
try
:
mye
(
0
)
# 觸發(fā)異常
except
Exception
as
err
:
print
1
,
err
else
:
print
2
#打印
1
Invalid level!
使用異常避免崩潰
發(fā)生錯誤時,如果程序還有工作沒有完成,妥善地處理錯誤就尤其重要。這種情況經(jīng)常會出現(xiàn)在要求用戶提供輸入的程序中;如果程序能夠妥善地處理無效輸入,就能再提示用戶提供有效輸入,而不至于崩潰。
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
)
這個程序提示用戶輸入一個數(shù)字,并將其存儲到變量first_number中;如果用戶輸入的不是表示退出的q,就再提示用戶輸入一個數(shù)字,并將其存儲到變量second_number中,接下來,我們計算這兩個數(shù)字的商。這個程序沒有采取任何處理錯誤的措施,因此讓它執(zhí)行除數(shù)為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 代碼塊
通過將可能引發(fā)錯誤的代碼放在
try-except
代碼塊中,可提高這個程序抵御錯誤的能力。錯誤是執(zhí)行除法運算的代碼行導(dǎo)致的,因此我們需要將它放到
try-except
代碼塊中。這個示例還包含一個else代碼塊;依賴于try代碼塊成功執(zhí)行的代碼都應(yīng)放到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嘗試執(zhí)行try代碼塊中的除法運算,這個代碼塊只包含可能導(dǎo)致錯誤的代碼。依賴于try代碼塊成功執(zhí)行的代碼都放在else代碼塊中;在這個示例中,如果除法運算成功,就使用else代碼塊來打印結(jié)果。
except代碼塊告訴Python,出現(xiàn)ZeroDivisionError異常時該怎么辦。如果try代碼塊因除零錯誤而失敗,我們就打印一條友好的消息,告訴用戶如何避免這種錯誤。程序?qū)⒗^續(xù)運行,用戶根本看不到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嘗試執(zhí)行try代碼塊中的代碼;只有可能引發(fā)異常的代碼才需要放在try語句中。有時候,有一些僅在try代碼塊成功執(zhí)行時才需要運行的代碼;這些代碼應(yīng)放在else代碼塊中。 except代碼塊告訴Python,如果它嘗試運行try代碼塊中的代碼時引發(fā)了指定的異常,該怎么辦。
通過預(yù)測可能發(fā)生錯誤的代碼,可編寫健壯的程序,它們即便面臨無效數(shù)據(jù)或缺少資源,也能繼續(xù)運行,從而能夠抵御無意的用戶錯誤和惡意的攻擊。
拋出異常
已看到如何使用 try 和 except 語句來處理 Python 的異常,這樣程序就可以從你預(yù)期的異常中恢復(fù)。
但你也可以在代碼中拋出自己的異常。拋出異常相當(dāng)于是說:“停止運行這個函數(shù)中的代碼,將程序執(zhí)行轉(zhuǎn)到 except 語句 ”
拋出異常使用 raise 語句。在代碼中, raise 語句包含以下部分:
- raise 關(guān)鍵字;
- 對 Exception 函數(shù)的調(diào)用;
- 傳遞給 Exception 函數(shù)的字符串,包含有用的出錯信息。
>>
>
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 語句,該程序就會崩潰,并顯示異常的出錯信息。
通常是調(diào)用該函數(shù)的代碼知道如何處理異常,而不是該函數(shù)本身。所以你常常會看到 raise 語句在一個函數(shù)中, try 和 except 語句在調(diào)用該函數(shù)的代碼中。 例如,打開一個新的文件編輯器窗口,輸入以下代碼,并保存為 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 異常
下面來看一種導(dǎo)致Python引發(fā)異常的簡單錯誤。你可能知道不能將一個數(shù)字除以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無法按你的要求做時,就會創(chuàng)建這種對象。在這種情況下, Python將停止運行程序,并指出引發(fā)了哪種異常,而我們可根據(jù)這些信息對程序進(jìn)行修改。下面我們將告訴Python,發(fā)生這種錯誤時怎么辦;這樣,如果再次發(fā)生這樣的錯誤,我們就有備無患了。
處理 FileNotFoundError 異常
使用文件時,一種常見的問題是找不到文件:你要查找的文件可能在其他地方、文件名可能不正確或者這個文件根本就不存在。對于所有這些情形,都可使用try-except代碼塊以直觀的方式進(jìn)行處理。
嘗試讀取一個不存在的文件。下面的程序嘗試讀取文件alice.txt的內(nèi)容,但我沒有將這個文件存儲在alice.py所在的目錄中:
#alice.py
filename
=
'alice.txt'
with
open
(
filename
)
as
f_obj
:
contents
=
f_obj
.
read
(
)
Python無法讀取不存在的文件,因此它引發(fā)一個異常:
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找不到要打開的文件時創(chuàng)建的異常。在這個示例中,這個
錯誤是函數(shù)open()導(dǎo)致的,
因此要處理這個錯誤,必須
將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代碼塊引發(fā)FileNotFoundError異常,因此Python找出與該錯誤匹配的except代碼塊,并運行其中的代碼。最終的結(jié)果是顯示一條友好的錯誤消息,而不是traceback:
Sorry, the file alice.txt does not exist.
斷言
????????“斷言”是一個心智正常的檢查,確保代碼沒有做什么明顯錯誤的事情。這些心智正常的檢查由
assert 語句
執(zhí)行。如果檢查失敗,就會拋出異常。在代碼中, assert語句包含以下部分:
- assert 關(guān)鍵字;
- 條件(即求值為 True 或 False 的表達(dá)式);
- 逗號;
- 當(dāng)條件為 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 設(shè)置為 ‘open’,所以從此以后,我們充分期望這個變量的值是 ‘open’。在使用這個變量的程序中,基于這個值是 ‘open’ 的假定,我們可能寫下了大量的代碼,即這些代碼依賴于它是 ‘open’,才能按照期望工作。所以添加了一個斷言,確保假定 podBayDoorStatus 是 ‘open’ 是對的。這里,我們加入了信息 ‘Thepod bay doors need to be “open”.’,這樣如果斷言失敗,就很容易看到哪里出了錯。
稍后,假如我們犯了一個明顯的錯誤,把另外的值賦給 podBayDoorStatus,但在很多行代碼中,我們并沒有意識到這一點。這個斷言會抓住這個錯誤,清楚地告訴我們出了什么錯。
在日常英語中, assert 語句是說:“我斷言這個條件為真,如果不為真,程序中什么地方就有一個缺陷。”不像異常,代碼不應(yīng)該用 try 和 except 處理 assert 語句。如果assert 失敗,程序就應(yīng)該崩潰。通過這樣的快速失敗,產(chǎn)生缺陷和你第一次注意到該缺陷之間的時間就縮短了。這將減少為了尋找導(dǎo)致該缺陷的代碼,而需要檢查的代碼量。
斷言針對的是程序員的錯誤,而不是用戶的錯誤。對于那些可以恢復(fù)的錯誤(諸如文件沒有找到,或用戶輸入了無效的數(shù)據(jù)),請拋出異常,而不是用 assert 語句檢測它。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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