當(dāng)一個(gè)異常在你的代碼中被引發(fā)時(shí),Python會(huì)打印一個(gè)traceback(回溯)。如果你是第一次看到回溯輸出,或者你不知道它在告訴你什么,那么它可能會(huì)讓你不知所措。但是Python回溯具有豐富的信息,可以幫助你診斷和修復(fù)代碼中引發(fā)異常的原因。理解Python回溯提供了什么信息對(duì)于成為一個(gè)更好的Python程序員至關(guān)重要。

在本教程結(jié)束時(shí),你將能夠:

理解你下一次遇到的回溯識(shí)別一些很常見的回溯在處理異常的同時(shí)成功記錄回溯

什么是Python回溯?

回溯是一個(gè)報(bào)告,其中包含在你的代碼中某個(gè)特定點(diǎn)上執(zhí)行的函數(shù)調(diào)用。回溯有很多名稱,包括堆棧跟蹤、堆棧回溯、向后追溯,也許還有其他名稱。在Python中,使用的術(shù)語(yǔ)是回溯。

當(dāng)你的程序引發(fā)一個(gè)異常時(shí),Python將打印當(dāng)前回溯信息以幫助你知道哪里出錯(cuò)了。下面是一個(gè)例子來(lái)說(shuō)明這種情況:

在這里,我們使用參數(shù)someone調(diào)用greet()。但是,在greet()中,這個(gè)變量名沒有被使用。相反,它在print()調(diào)用中被錯(cuò)誤拼寫為someon。

注意:本教程假設(shè)你理解Python異常。如果你不熟悉或者只是想復(fù)習(xí)一下,那么你應(yīng)該查看《Python異常:介紹》。

當(dāng)你運(yùn)行這個(gè)程序時(shí),你會(huì)得到以下回溯:

這里,你已經(jīng)設(shè)置了另一個(gè)Python文件,該文件將導(dǎo)入前面的模塊greetings.py,并從中使用greet()。下面是運(yùn)行example.py時(shí)會(huì)發(fā)生的事情:

這將會(huì)打印出對(duì)所有三個(gè)人的問候語(yǔ)。但是,如果你運(yùn)行這段代碼,你會(huì)看到一個(gè)輸出多個(gè)回溯的例子:

在上面的例子中,你可能期望a_list是list類型的,它有一個(gè)名為.Append()的方法。當(dāng)你接收到AttributeError異常并看到它是在你嘗試調(diào)用.append()時(shí)引發(fā)的,這說(shuō)明你正在處理的對(duì)象類型可能不是你所期望的。

通常,當(dāng)你期望從一個(gè)函數(shù)或方法調(diào)用返回一個(gè)特定類型的對(duì)象時(shí),會(huì)出現(xiàn)這種情況,你最終會(huì)得到一個(gè)類型為None的對(duì)象。在本例中,錯(cuò)誤消息行將寫到,AttributeError: 'None類型'對(duì)象沒有屬性'append'。

ImportError

當(dāng)一個(gè)import語(yǔ)句出錯(cuò)時(shí),ImportError會(huì)被引發(fā)。如果你試圖導(dǎo)入的模塊找不到,或者你試圖從一個(gè)模塊中導(dǎo)入模塊中不存在的內(nèi)容時(shí),你將得到這個(gè)異常,或者它的子類ModuleNotFoundError。Python文檔定義了此異常何時(shí)被引發(fā):

當(dāng)import語(yǔ)句在嘗試加載模塊時(shí)遇到困難時(shí)引發(fā)。當(dāng)from…import中的“from list”中存在一個(gè)無(wú)法被找到的名稱時(shí)也會(huì)引發(fā)。

下面是一個(gè)ImportError 和ModuleNotFoundError被引發(fā)的例子。

KeyError的錯(cuò)誤消息行會(huì)給出找不到的鍵。這并沒有太多的內(nèi)容,但是,結(jié)合回溯的其他內(nèi)容,但對(duì)于修復(fù)這個(gè)問題來(lái)說(shuō)通常是足夠了。

要深入了解KeyError,請(qǐng)查看《Python KeyError異常以及如何處理它們》。

NameError

當(dāng)你引用了一個(gè)代碼中未定義的變量、模塊、類、函數(shù)或其他名稱時(shí),將引發(fā)一個(gè)NameError。Python文檔定義了此異常何時(shí)被引發(fā):

當(dāng)本地或全局名稱未被找到時(shí)引發(fā)。

在下面的代碼中,greet()接受一個(gè)參數(shù)person。但在函數(shù)本身中,該參數(shù)被錯(cuò)誤拼寫為persn:

SyntaxError的錯(cuò)誤消息行只告訴你代碼的語(yǔ)法有問題。查看上面的行可以得到問題所在的行,通常用a ^(插入符號(hào))指向問題點(diǎn)。這里,函數(shù)的def語(yǔ)句中缺少冒號(hào)。

同樣,使用SyntaxError回溯,常規(guī)的第一行Traceback (most recent call last:也丟失了。這是因?yàn)楫?dāng)Python試圖解析你的代碼時(shí),SyntaxError會(huì)被引發(fā),而實(shí)際上這些行并沒有被執(zhí)行。

TypeError

當(dāng)你的代碼試圖對(duì)一個(gè)對(duì)象執(zhí)行某些不能執(zhí)行的操作時(shí),例如試圖將一個(gè)字符串相加到一個(gè)整數(shù)中,或者在一個(gè)沒有定義其長(zhǎng)度的對(duì)象上調(diào)用len(),TypeError就會(huì)被引發(fā)。Python文檔中定義了此異常何時(shí)被引發(fā):

當(dāng)一個(gè)操作或函數(shù)被應(yīng)用于一個(gè)不合適類型的對(duì)象時(shí)引發(fā)。

下面是TypeError被引發(fā)的幾個(gè)示例:

以上所有引發(fā)TypeError的示例都會(huì)產(chǎn)生一個(gè)包含不同消息的錯(cuò)誤消息行。每一條消息都能很好地告訴你哪里出了問題。

前兩個(gè)示例嘗試將字符串和整數(shù)相加。然而,它們有細(xì)微的不同:

第一個(gè)試圖將一個(gè)str加到一個(gè)int。第二個(gè)試圖將一個(gè)int 加到一個(gè) str。

錯(cuò)誤消息行反映了這些不同。

最后一個(gè)例子嘗試在一個(gè)int上調(diào)用len()。錯(cuò)誤消息行告訴你不能對(duì)一個(gè)int類型執(zhí)行此操作。

ValueError

當(dāng)對(duì)象的值不正確時(shí),ValueError將被引發(fā)。你可以將其視為一個(gè)IndexError,當(dāng)索引值不在序列范圍之內(nèi)時(shí)會(huì)被引發(fā),只不過ValueError用于更一般的情況。Python文檔中定義了此異常何時(shí)被引發(fā):

當(dāng)一個(gè)操作或函數(shù)接收到一個(gè)具有正確類型但值不合適的參數(shù)時(shí)引發(fā),并且這種情況不能被一個(gè)更精確的異常(比如IndexError)描述。

下面是ValueError被引發(fā)的兩個(gè)例子:

在這些例子中,ValueError錯(cuò)誤消息行會(huì)準(zhǔn)確地告訴你這些值存在什么問題:

在第一個(gè)示例中,你試圖解壓縮太多的值。錯(cuò)誤消息行甚至告訴你,你期望解壓縮3個(gè)值,但是只得到了2個(gè)值。在第二個(gè)例子中,問題是你得到了太多的值,但沒有足夠的變量來(lái)解壓縮它們。

如何記錄一個(gè)回溯?

獲得異常及其生成的Python回溯意味著你需要決定如何處理它。通常,修復(fù)代碼是第一步,但有時(shí)問題出在未預(yù)期的或不正確的輸入上。雖然在代碼中提供這些情況很好,但有時(shí)通過記錄回溯和執(zhí)行其他操作來(lái)隱藏異常也很有意義。

下面是一個(gè)更真實(shí)的代碼示例,它需要讓一些Python回溯保持靜默。本例使用了requests庫(kù)。你可以在Python的requests庫(kù)(指南)中獲取更多信息:

這段代碼運(yùn)行得很好。當(dāng)你運(yùn)行此腳本時(shí),你將一個(gè)URL作為命令行參數(shù)提供給它,它將調(diào)用該URL,然后打印出HTTP狀態(tài)碼和響應(yīng)中的內(nèi)容。甚至在響應(yīng)是一個(gè)HTTP錯(cuò)誤狀態(tài)時(shí),它也可以工作:

但是,有時(shí)你的腳本提供的用于檢索的URL不存在,或者主機(jī)服務(wù)器關(guān)閉。在這些情況下,這個(gè)腳本現(xiàn)在就會(huì)引發(fā)一個(gè)未捕獲的ConnectionError異常,并打印一個(gè)回溯:

這里的Python回溯可能非常長(zhǎng),還會(huì)引發(fā)許多其他異常,最終導(dǎo)致ConnectionError被requests庫(kù)本身引發(fā)。如果你向上移動(dòng)到最后的異?;厮?,你就可以看到問題都是從我們的代碼urlcall .py中的第5行開始的。

如果你將非法行封裝在一個(gè)try和except塊中,那么捕獲適當(dāng)?shù)漠惓⒃试S你的腳本繼續(xù)處理更多的輸入:

上面的代碼使用了一個(gè)帶有try和except塊的else子句。如果你不熟悉Python的這一特性,那么請(qǐng)?jiān)赑ython Exceptions:An Introduction中查看else子句。

現(xiàn)在,當(dāng)你使用一個(gè)URL來(lái)運(yùn)行此腳本時(shí),將引發(fā)一個(gè)ConnectionError,系統(tǒng)會(huì)打印一個(gè)狀態(tài)碼-1,以及Connection Error的內(nèi)容:

這運(yùn)行的很好。然而,在大多數(shù)實(shí)際系統(tǒng)中,你并不希望只是靜默化異常和生成的回溯,而是希望去記錄回溯。記錄回溯可以讓你更好地理解程序中哪些地方出錯(cuò)了。

注意: 要了解更多關(guān)于Python日志系統(tǒng)的信息,請(qǐng)查看Python中的logging。

你可以通過導(dǎo)入logging包,獲取一個(gè)日志記錄器并在try和except塊的except部分中調(diào)用該日志記錄器的.exception()來(lái)在你的腳本中記錄回溯。你的最終腳本應(yīng)該會(huì)類似于以下代碼:

現(xiàn)在,當(dāng)你對(duì)一個(gè)有問題的URL運(yùn)行此腳本時(shí),它會(huì)打印預(yù)期的-1和Connection Error,同時(shí)也會(huì)記錄回溯:

默認(rèn)情況下,Python將向標(biāo)準(zhǔn)錯(cuò)誤(stderr)發(fā)送日志消息。看起來(lái)我們根本沒有抑制回溯輸出。但是,如果你在重定向stderr時(shí)再次調(diào)用它,你可以看到日志系統(tǒng)正在工作,我們可以將日志保存起來(lái),以備以后使用:

結(jié)論

Python回溯包含了大量的信息,可以幫助你發(fā)現(xiàn)你的Python代碼中出現(xiàn)的錯(cuò)誤。這些回溯看起來(lái)有點(diǎn)嚇人,但是一旦你把它分解開來(lái),看看它想向你展示什么,它們就會(huì)非常有用。逐行瀏覽一些回溯將會(huì)使你更好地理解它們包含的信息,并幫助你最大限度地利用它們。

在運(yùn)行代碼時(shí)獲得Python回溯輸出是改進(jìn)代碼的一個(gè)機(jī)會(huì)。這是Python試圖幫助你的一種方式。

既然你已經(jīng)了解了如何閱讀Python回溯,那么你可以從學(xué)習(xí)更多有關(guān)診斷回溯輸出所告訴你的問題的一些工具和技術(shù)中獲益。Python的內(nèi)置traceback模塊可用于處理和檢查回溯。當(dāng)你需要從回溯輸出中獲得更多信息時(shí),traceback模塊是很有用的。了解更多有關(guān)調(diào)試Python代碼的技術(shù)也會(huì)很有幫助。

1.《back怎么讀 什么是Python回溯? Traceback(回溯)》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識(shí),僅代表作者本人觀點(diǎn),與本網(wǎng)站無(wú)關(guān),侵刪請(qǐng)聯(lián)系頁(yè)腳下方聯(lián)系方式。

2.《back怎么讀 什么是Python回溯? Traceback(回溯)》僅供讀者參考,本網(wǎng)站未對(duì)該內(nèi)容進(jìn)行證實(shí),對(duì)其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。

3.文章轉(zhuǎn)載時(shí)請(qǐng)保留本站內(nèi)容來(lái)源地址,http://f99ss.com/keji/347786.html