@[toc]
??編寫計算機(jī)程序時洋满,通常能夠區(qū)分正常和異常情況。異常事件可能是錯誤,也可能是通常不會發(fā)生的事情胀茵。為處理這些異常事件吐句,Python提供功能強(qiáng)大的異常處理機(jī)制。
一店读、異常是什么
??Python使用異常對象來表示異常狀態(tài)嗦枢,并在遇到錯誤時引發(fā)異常。異常對象未被處理(或捕獲)時屯断,程序?qū)⒔K止并顯示一條錯誤消息(traceback
)文虏。
>>> 1/0
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
1/0
ZeroDivisionError: division by zero
??事實上,每個異常都是某個類(這里是ZeroDivisionError
)的實例殖演。你能以各種方式引發(fā)和捕獲這些實例氧秘,從而逮住錯誤并采取措施,而不是讓程序失敗
二趴久、讓事情沿你指定的軌道出錯
??正如你看到的丸相,出現(xiàn)問題時,將自動引發(fā)異常彼棍。先來看看如何自主地引發(fā)異常灭忠,還有如何創(chuàng)建異常,然后再學(xué)習(xí)如何處理這些異常座硕。
1. raise語句
??Python使用raise語句
拋出一個指定異常弛作。要引發(fā)異常,可使用raise語句
华匾,并將一個類(必須是Exception
的子類)或?qū)嵗鳛閰?shù)映琳。將類作為參數(shù)時,將自動創(chuàng)建一個實例。下面的示例使用的是內(nèi)置異常類Exception
:
>>> raise Exception
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
raise Exception
Exception
??這里引發(fā)的是通用異常萨西,并沒有指出出現(xiàn)了什么錯誤有鹿。
>>> raise Exception('hyperdrive overload')
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
raise Exception('hyperdrive overload')
Exception: hyperdrive overload
??這里添加了錯誤信息hyperdrive overload
。
??在很多內(nèi)置的異常類原杂,表描述了最重要的幾個印颤。
<center>一些內(nèi)置的異常類</center>
類名 | 描述 |
---|---|
Exception | 幾乎所有的異常類都是從它派生而來的 |
AttributeError | 引用屬性或給它賦值失敗時引發(fā) |
OSError | 操作系統(tǒng)不能執(zhí)行指定的任務(wù)(如打開文件)時引發(fā),有多個子類 |
IndexError | 使用序列中不存在的索引時引發(fā)穿肄,為LookupError的子類 |
KeyError | 使用映射中不存在的鍵時引發(fā)年局,為LookupError的子類 |
NameError | 找不到名稱(變量)時引發(fā) |
SyntaxError | 代碼不正確時引發(fā) |
TypeError | 將內(nèi)置操作或函數(shù)用于類型不正確的對象時引發(fā) |
ValueError | 將內(nèi)置操作或函數(shù)用于這樣的對象時引發(fā):其類型正確但包含的值不合適 |
ZeroDivisionError | 在除法或求模運算的第二個參數(shù)為零時引發(fā) |
2. 自定義的異常類
??我們可以像創(chuàng)建其他類一樣,但務(wù)必直接或間接地繼承Exception
(這意味著從任何內(nèi)置異常類派生都可以)咸产。因此矢否,自定義異常類的代碼類似于下面這樣:
class SomeCustomException(Exception): pass
??因為錯誤就是類,捕獲一個錯誤就是捕獲一個該類的實例脑溢,因此錯誤并不是憑空產(chǎn)生的僵朗,而是由一些不合理的部分導(dǎo)致的。Python的內(nèi)置函數(shù)會拋出很多類型的錯誤屑彻,我們自己編寫的函數(shù)也可以拋出錯誤验庙。如果要拋出錯誤,那么可以根據(jù)需要定義一個錯誤的類社牲,選擇好繼承關(guān)系粪薛,然后用raise語句
拋出一個錯誤的實例。
??例如:
class MyError(Exception):
def __init__(self):
pass
def __str__(self):
return 'this is self define error'
def my_error_test():
try:
raise MyError()
except MyError as e:
print('exception info:',e)
my_error_test()
??執(zhí)行結(jié)果如下:
exception info:this is self define error
??程序正確地執(zhí)行了自定義的異常搏恤。
三违寿、捕獲異常
??異常比較有趣的地方是可對其進(jìn)行處理,通常稱之為捕獲異常熟空。為此藤巢,可使用try/except語句
。try/except語句
用來檢測try語句中的錯誤息罗,從而讓except語句
捕獲異常信息并處理掂咒。假設(shè)你創(chuàng)建了一個程序,讓用戶輸入兩個數(shù)迈喉,再將它們相除俏扩,如下所示:
>>> x = int(input('Enter the first number:'))
>>> y = int(input('Enter the second number'))
>>> print(x / y)
??如果第二個數(shù)字為零的話,就會報錯弊添。為了捕獲這種異常并對錯誤進(jìn)行處理录淡,可像下面這樣重寫程序。
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
print(x / y)
except ZeroDivisionError:
print("第二個數(shù)字不能為0")
??try
的工作原理是油坝,開始一個try語句
后嫉戚,Python就在當(dāng)前程序的上下文中做標(biāo)記刨裆,當(dāng)出現(xiàn)異常時就可以回到做標(biāo)記的地方。首先執(zhí)行try語句
彬檀,接下來發(fā)生什么依賴于執(zhí)行時是否出現(xiàn)異常帆啃。
??如果try
后的語句執(zhí)行時發(fā)生異常,程序就跳回try并執(zhí)行except子句
窍帝。異常處理完畢努潘,控制流就可以通過整個try語句
了(除非在處理異常時又引發(fā)新異常)。
??注意:異常從函數(shù)向外傳播到調(diào)用函數(shù)的地方坤学。如果在這里也沒有被捕獲疯坤,異常將向程序的最頂層傳播。這意味著你可使用try/except來捕獲他人所編寫函數(shù)引發(fā)的異常深浮。詳見第4小節(jié)压怠。
1. 不用提供參數(shù)
??捕獲異常后,如果要重新引發(fā)它(即繼續(xù)向上傳播)飞苇,可調(diào)用raise
且不提供任何參數(shù)(也可顯式地提供捕獲到的異常菌瘫,參見(4))。
>>> raise
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
raise
RuntimeError: No active exception to reraise
??為說明這很有用布卡,來看一個能夠抑制異常ZeroDivisionError
的計算器類雨让。如果啟用了這種功能,計算器將打印一條錯誤消息忿等,而不讓異常繼續(xù)傳播宫患。在與用戶交互的會話中使用這個計算器時,抑制異常很有用这弧;但在程序內(nèi)部使用時,引發(fā)異常是更佳的選擇(此時應(yīng)關(guān)閉抑制功能)虚汛。
??下面來看一個類的代碼:
class MuffledCalculator:
muffled = False
def calc(self, expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print('Division by zero is illegal')
else:
raise
# eval()將字符串str當(dāng)成有效的表達(dá)式來求值并返回計算結(jié)果
??注意:發(fā)生除零行為時匾浪,如果啟用了抑制功能,方法calc
將(隱式地)返回None卷哩。換而言之蛋辈,如果啟用了抑制功能,就不應(yīng)依賴返回值将谊。
??下面的示例演示了這個類的用法(包括啟用和關(guān)閉了抑制功能的情形):
>>> calculator = MuffledCalculator()
>>> calculator.calc('10/2')
5.0
>>> calculator.calc('10/0') #關(guān)閉了抑制功能
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
calculator.calc('10/0')
File "<pyshell#15>", line 5, in calc
return eval(expr)
File "<string>", line 1, in <module>
ZeroDivisionError: division by zero
>>> calculator.muffled = True #開啟了抑制功能
>>> calculator.calc('10/0')
Division by zero is illegal
??如你所見冷溶,關(guān)閉抑制功能時,捕獲了異常ZeroDivisionError
尊浓,但繼續(xù)向上傳播它逞频。
??如果無法處理異常,在except子句
中使用不帶參數(shù)的raise
通常是不錯的選擇栋齿,但有時你可能想引發(fā)別的異常苗胀。在這種情況下襟诸,導(dǎo)致進(jìn)入except子句
的異常將被作為異常上下文存儲起來,并出現(xiàn)在最終的錯誤消息中基协。
>>> try:
1/0
except ZeroDivisionError:
raise ValueError
Traceback (most recent call last):
File "<pyshell#28>", line 2, in <module>
1/0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#28>", line 4, in <module>
raise ValueError
??你可使用raise…from…
語句來提供自己的異常上下文歌亲,也可使用None來禁用上下文。
>>> try:
1/0
except ZeroDivisionError:
raise ValueError from None
Traceback (most recent call last):
File "<pyshell#30>", line 4, in <module>
raise ValueError from None
ValueError
2. 多個except子句
??如果你運行前一節(jié)的程序澜驮,并在提示時輸入一個非數(shù)字值陷揪,將引發(fā)另一種異常。
Enter the first number: 10
Enter the second number: 'Hello'
Traceback (most recent call last):
File "<pyshell#33>", line 3, in <module>
y = int(input('Enter the second number'))
ValueError: invalid literal for int() with base 10: "'hello'"
??由于該程序中的except子句
只捕獲ZeroDivisionError異常
杂穷,這種異常將成為漏網(wǎng)之魚悍缠,導(dǎo)致程序終止。為同時捕獲這種異常亭畜,可在try/except
語句中再添加一個except子句
扮休。
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
print(x / y)
except ZeroDivisionError:
print("第二個數(shù)字不能為0")
except ValueError:
print("這不是一個數(shù)字")
??try語句按照如下方式工作:
??首先,執(zhí)行try子句(在關(guān)鍵字try和關(guān)鍵字except之間的語句)拴鸵。
??如果沒有發(fā)生異常玷坠,忽略except子句,try子句執(zhí)行后結(jié)束劲藐。如果在執(zhí)行try子句的過程中發(fā)生異常八堡,try子句余下的部分會被忽略。如果異常的類型和except之后的名稱相符聘芜,對應(yīng)的except子句就會被執(zhí)行兄渺。最后執(zhí)行try語句之后的代碼。如果沒有一個異常與任何except匹配汰现,這個異常就會傳遞到上層的try中挂谍。一個try語句可能包含多個except子句,分別處理不同的異常瞎饲,但最多只有一個分支會被執(zhí)行口叙。
??處理程序?qū)⒅会槍?yīng)try子句
中的異常進(jìn)行處理,而不會處理其他異常語句中的異常。
??注意到異常處理并不會導(dǎo)致代碼混亂,而添加大量的if語句來檢查各種可能的錯誤狀態(tài)將導(dǎo)致代碼的可讀性極差蒋搜。
3. 一箭雙雕
??如果要使用一個except子句
捕獲多種異常垢啼,可在一個元組中指定這些異常,如下所示:
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
print(x / y)
except (ZeroDivisionError, TypeError, ValueError, NameError):
print("你的數(shù)字是偽造的")
??在上述代碼中,如果用戶輸入字符串、其他非數(shù)字值或輸入的第二個數(shù)為零,都將打印同樣的錯誤消息启具。當(dāng)然,僅僅打印錯誤消息幫助不大珊泳。另一種解決方案是不斷地要求用戶輸入數(shù)字富纸,直到能夠執(zhí)行除法運算為止囤踩,(6)將介紹如何這樣做。
??這樣做有什么好處呢晓褪?假如我們希望多個except子句
輸出同樣的信息堵漱,就沒有必要在幾個except子句
中重復(fù)輸入語句,放到一個異常塊中即可涣仿。
??在except子句
中勤庐,異常兩邊的圓括號很重要。一種常見的錯誤是省略這些括號好港,這可能導(dǎo)致你不想要的結(jié)果愉镰,其中的原因請參閱下一節(jié)。
4. 捕獲對象
??要在except子句
中訪問異常對象本身钧汹,可使用兩個而不是一個參數(shù)丈探。(請注意,即便是在你捕獲多個異常時拔莱,也只向except
提供了一個參數(shù)—一個元組碗降。)需要讓程序繼續(xù)運行并記錄錯誤(可能只是向用戶顯示)時,這很有用塘秦。如果希望在except子句
中訪問異常對象本身讼渊,也就是看到一個異常對象真正的異常信息,而不是輸出自己定義的異常信息尊剔,可以使用as e
的形式爪幻,我們稱之為捕捉對象。
??下面的示例程序打印發(fā)生的異常并繼續(xù)運行:
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
print(x / y)
except (ZeroDivisionError, ValueError) as e:
print(e)
#division by zero
#invalid literal for int() with base 10: "'s'"
??在這個小程序中须误,except子句
也捕獲兩種異常挨稿,但由于你同時顯式地捕獲了對象本身,因此可將其打印出來京痢,讓用戶知道發(fā)生了什么情況奶甘。(6)將介紹這種技術(shù)的另一種更有用的用途。
5. 一網(wǎng)打盡
??即使程序處理了好幾種異常历造,還是可能有一些漏網(wǎng)之魚。例如船庇,對于前面執(zhí)行除法運算的程序吭产,如果用戶在提示時不輸入任何內(nèi)容就按回車鍵,將出現(xiàn)一條錯誤消息鸭轮,還有一些相關(guān)問題出在什么地方的信息(棧跟蹤)臣淤,如下所示:
Traceback (most recent call last):
…
invalid literal for int() with base 10: ''
??這種異常未被try/except語句
捕獲,因為你沒有預(yù)測到這種問題窃爷,也沒有采取相應(yīng)的措施邑蒋。在這些情況下姓蜂,與其使用并非要捕獲這些異常的try/except語句
將它們隱藏起來,還不如讓程序馬上崩潰医吊,因為這樣你就知道什么地方除了問題钱慢。
??然而,如果你就是要使用一段代碼捕獲所有的異常卿堂,只需在except語句
中不指定任何異常類即可束莫。
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
print(x / y)
except:
print('Something wrong happened …')
??現(xiàn)在,用戶輸入什么錯誤信息都可以報錯草描±缆蹋可以在except子句
中忽略所有異常類,從而讓程序輸出自己定義的異常信息穗慕。
??但是饿敲,像這樣捕獲所有的異常很危險,因為這不僅會隱藏你有心理準(zhǔn)備的錯誤逛绵,還會隱藏你沒有考慮過的錯誤怀各。這還將捕獲用戶使用Ctrl + C
終止執(zhí)行的企圖、調(diào)用函數(shù)sys.exit
來終止執(zhí)行的企圖等暑脆。在大多數(shù)情況下渠啤,更好的選擇是使用except Exception as e
并對異常對象進(jìn)行檢查。這樣做將讓不是從Exception
派生而來的為數(shù)不多的異常成為漏網(wǎng)之魚添吗,其中包括SystemExit
和KeyboardInterrupt
沥曹,因為它們是從BaseException(Exception的超類)
派生而來的。
6. 萬事大吉時
??在有些情況下碟联,在沒有出現(xiàn)異常時執(zhí)行一個代碼塊很有用妓美。為此,可像條件語句和循環(huán)一樣鲤孵,給try/except語句
添加一個else子句
壶栋。
try:
print('A simple tasl')
except:
print('what? Something went wrong?')
else:
print('Ah … It went as planned.')
??如果你運行這些代碼,輸出如下:
A simple tasl
Ah … It went as planned.
??通過使用else子句
普监,可實現(xiàn)3.(3)所說的循環(huán)
while True:
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
value x / y
print('x / y is ', value)
except:
print('Invalid input. Please try again')
else:
break
??在這里贵试,僅當(dāng)沒有引發(fā)異常時,才會跳出循環(huán)凯正。換而言之毙玻,只要出現(xiàn)錯誤,程序就會要求用戶提供新的輸入廊散。
??前面說過桑滩,一種更佳的替代方案是使用空的except子句
來捕獲所有屬于類Exception
的異常。你不能完全確定這將捕獲所有的異常允睹,因為try/except語句
中的代碼可能使用舊式的字符串異吃俗迹或引發(fā)并非從Exception
派生而來的異常幌氮。然而,如果使用except Exception as e
胁澳,就可利用3.(4)節(jié)介紹的技巧在這個小型除法程序中打印更有用的錯誤消息该互。
while True:
try:
x = int(input('Enter the first number:'))
y = int(input('Enter the second number:'))
value x / y
print('x / y is ', value)
except Exception as e:
print('Invalid input:', e)
print('please try again!')
else:
break
??如果在try子句
執(zhí)行時沒有發(fā)生異常,就會執(zhí)行else語句
后面的語句听哭。使用else子句
比把所有語句都放在try子句
里更好慢洋,這樣可以避免一些意想不到而except
又沒有捕獲的異常。
??當(dāng)程序沒有發(fā)生異常時陆盘,通過添加一個else子句
做一些事情(比如輸出一些信息)很有用普筹,可以幫助我們更好地判斷程序的執(zhí)行情況。
7. 最后
??最后隘马,還有finally子句
太防,可用于在發(fā)生異常時執(zhí)行清理工作。這個子句是與try子句
配套的酸员,try/finally語句
無論發(fā)生異常與否都將執(zhí)行最后的代碼蜒车。
x = None
try:
x = 1 / 0
finally:
print('Cleaning up!不管怎么樣我都會先展示在最前面')
del x
??在上述示例中,finally子句
被執(zhí)行了幔嗦,不管try子句
中發(fā)生什么異常酿愧,都將執(zhí)行finally子句
。為何在try子句
之前初始化x
呢邀泉?因為如果不這樣做嬉挡,ZeroDivisionError
將導(dǎo)致根本沒有機(jī)會給它賦值,進(jìn)而導(dǎo)致在finally子句
中對其執(zhí)行del
時引發(fā)未捕獲的異常汇恤。
??如果運行這個程序庞钢,它將在執(zhí)行清理工作后崩潰。
??Cleaning up! 不管怎么樣我都會先展示在最前面
Traceback (most recent call last):
File "<pyshell#42>", line 2, in <module>
x = 1 / 0
ZeroDivisionError: division by zero
??雖然使用del
來刪除變量是相當(dāng)愚蠢的清理措施因谎,但finally子句
非常適合用于確保文件或網(wǎng)絡(luò)套接字等得以關(guān)閉基括,這將在第14章介紹。
??也可在一條語句中同時包含try
财岔、except
风皿、finally
和else
(或其中的3個),但要記得else
在except
之后匠璧,finally在except
和else
之后桐款。
try:
1/0
except ZeroDivisionError:
print('Division by zero')
else:
print('That went well')
finally:
print('cleaning up.')
#Division by zero
#cleaning up.
finally子句在關(guān)閉文件或數(shù)據(jù)庫連接時非常有用。
8. 補(bǔ)充和總結(jié)
??注意:如果拋出父類異常患朱,在子類不會再獲取鲁僚。
??如下:
try:
fun()
except Exception as e:
raise Exception
except ImportError as e:
raise ImportError
finally:
pass
??在上面的例子中炊苫,下面的ImportError
就不會被拋出裁厅,應(yīng)為ImportError
繼承Exception
冰沙,但是可以把Exception
放在后面是可以的
try:
suite1 #測試語句塊
except exception1:
suite2 #如果測試語句suite1中發(fā)生exception1異常時執(zhí)行
except (exception2,exception3):
suite3 #如果測試語句suite1中發(fā)生元組中任意異常時執(zhí)行
except exception4 as reason: #as把異常的原因賦值給reason
suite4 #如果測試語句suite1發(fā)生exception4的異常時執(zhí)行
except:
suite5 #如果測試語句suite1發(fā)生異常在所列出的異常之外時執(zhí)行
else:
suite5 #如果測試語句塊suite1中沒有發(fā)生異常時執(zhí)行
finally:
suit6 #不管測試語句suite1中又沒有發(fā)生異常都會執(zhí)行
??所有錯誤類型都繼承自BaseException
四、異常和函數(shù)
??異常和函數(shù)有著天然的聯(lián)系执虹。如果不處理函數(shù)中引發(fā)的異常拓挥,它將向上傳播到調(diào)用函數(shù)的地方。如果在那里也未得到處理袋励,異常將繼續(xù)傳播侥啤,直至到達(dá)主程序(全局作用域)。如果主程序中也沒有異常處理程序茬故,程序?qū)⒔K止并顯示棧跟蹤消息盖灸。來看一個示例。
>>> def faulty():
raise Exception('Something is wrong')
>>> def ignore_exception():
faulty()
>>> def handle_exception():
try:
faulty()
except:
print('Exception handled')
>>> ignore_exception()
??結(jié)果如下
Traceback (most recent call last):
File "C:/Users/MIC/Desktop/test1.py", line 11, in <module>
ignore_exception()
File "C:/Users/MIC/Desktop/test1.py", line 4, in ignore_exception
faulty()
File "C:/Users/MIC/Desktop/test1.py", line 2, in faulty
raise Exception('Something is wrong')
Exception: Something is wrong
>>> handle_exception()
Exception handled
??如你所見磺芭,faulty
中引發(fā)的異常依次從faulty
和ignore_exception
向外傳播赁炎,最終導(dǎo)致顯示一條棧跟蹤消息。調(diào)用handle_exception
時钾腺,異常最終傳播到handle_exception
,并被這里的try/except語句
處理徙垫。
??注意:異常信息是以堆棧的形式被拋出的,因而是從下往上查看的放棒。所謂堆棧姻报,就是最先被發(fā)現(xiàn)的異常信息最后被輸出,也被稱作先進(jìn)后出间螟。
五吴旋、異常之禪
??異常處理并不是很復(fù)雜。如果你知道代碼可能引發(fā)某種異常寒亥,且不希望出現(xiàn)這種異常時程序終止并顯示站跟蹤消息邮府,可添加必要的try/except或try/finally語句(或結(jié)合使用)來處理它。
??有時候溉奕,可使用條件語句來達(dá)成異常處理實現(xiàn)的目標(biāo)褂傀,但這樣編寫出來的代碼可能不那么自然,可讀性也沒那么高加勤。另一方面仙辟,有些任務(wù)使用if/else完成時看似很自然,但實際上使用try/except來完成要好得多鳄梅。下面來看兩個示例叠国。
??假設(shè)有一個字典,你要在指定的鍵存在時打印與之相關(guān)聯(lián)的值戴尸,否則什么都不做粟焊。實現(xiàn)這種功能的代碼可能類似于下面這樣:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
if 'occupation' in person;
print('Occupation:', person['occupation'])
??如果你調(diào)用這個函數(shù),并向它提供一個包含姓名Throatwobbler Mangrove
和年齡42的字典,輸出將如下:
Description of Throatwobbler Mangrove
Age: 42
??如果你在這個字典中添加職業(yè)camper
项棠,輸出將如下:
Description of Throatwobbler Mangrove
Age: 42
Occupation: camper
??這段代碼很直觀悲雳,但效率不高,因為它必須兩次查找'Occupation'
鍵:一次檢查這個鍵是否存在(在條件中)香追,另一次獲取這個鍵關(guān)聯(lián)的值合瓢,以便將其打印出來。下面是另一種解決方案:
def describe_person(person):
print('Description of', person['name'])
print('Age:', person['age'])
try:
print('Occupation:', person['occupation'])
except KeyError: pass
??在這里透典,函數(shù)直接假設(shè)存在'Occupation:'
鍵晴楔。如果這種假設(shè)正確,就能省點事:直接獲取并打印值峭咒,而無需檢查這個鍵是否存在税弃。如果這個鍵不存在,將引發(fā)KeyError
凑队,而except子句
將捕獲這個異常钙皮。
??你可能發(fā)現(xiàn),檢查對象是否包含特定的屬性時顽决,try/except
也很有用短条。例如,要檢查一個對象是否包含屬性write
,可使用類似于下面的代碼:
try:
obj.write
except AttributeError:
print('The object is not writeable')
else:
print('The object is writeable')
??在這里才菠,try子句
只是訪問屬性write
茸时,而沒有使用它來做任何事情。如果引發(fā)了AttributeError異常
赋访,說明對象沒有屬性write
可都,否則就說明有這個屬性。這種解決方案可代替7.2.9節(jié)介紹的使用getattr
的解決方案蚓耽,而且更自然渠牲。具體使用哪種解決方案,在很大程度上取決于個人喜好步悠。
??在很多情況下签杈,相比于使用if/else
,使用try/except語句
更自然鼎兽,也更符合Python的風(fēng)格答姥。因此你應(yīng)養(yǎng)成盡可能使用try/except語句
的習(xí)慣。
六谚咬、不那么異常的情況
??如果你只想發(fā)出警告鹦付,指出情況偏離了正規(guī),可使用模塊warnings
中的函數(shù)warn
择卦。
>>> from warnings import warn
>>> warn("I've got a bad feeling about this.")
Warning (from warnings module):
File "C:/Users/MIC/Desktop/test1.py", line 1
def faulty():
UserWarning: I've got a bad feeling about this.
??警告只顯示一次敲长。如果再運行最后一行代碼郎嫁,什么事情都不會發(fā)生。
??如果其他代碼在使用你的模塊祈噪,可使用模塊warning
中的函數(shù)filterwarning
來抑制你發(fā)出的警告(或特定類型的警告)行剂,并指定要采取的措施,如"error"或"ignore"钳降。
>>> from warnings import filterwarnings
>>> filterwarnings("ignore")
>>> warn("Anyone out there?")
>>> filterwarnings("error")
>>> warn("Something is very wrong!")
Traceback (most recent call last):
File "<pyshell#51>", line 1, in <module>
warn("Something is very wrong!")
UserWarning: Something is very wrong!
??如你所見,引發(fā)的異常為UserWaining
腌巾。發(fā)出警告時遂填,可指定將引發(fā)的異常(即警告類型),但必須是Warning的子類
澈蝙。如果將警告轉(zhuǎn)換為錯誤吓坚,將使用你指定的異常。另外灯荧,還可根據(jù)異常來過濾特定類型的警告礁击。
>>> filterwarnings("error")
>>> warn("This function is really old ...", DeprecationWarning) #指定異常
Traceback (most recent call last):
File "<pyshell#54>", line 1, in <module>
warn("This function is really old ...", DeprecationWarning)
DeprecationWarning: This function is really old ...
>>> filterwarnings("ignore", category=DeprecationWarning) #指定過濾掉的異常
>>> warn("Another deprecation warning.", DeprecationWarning)
>>> warn("Something else.")
Traceback (most recent call last):
File "<pyshell#57>", line 1, in <module>
warn("Something else.")
UserWarning: Something else.
??除上述基本用途外,模塊warnings
還提供了一些高級功能逗载。