前面幾小節(jié)介紹了調(diào)試 Python 程序如何調(diào)置斷點(diǎn)、如何啟動(dòng)一個(gè)調(diào)試器,以及查看變量值等功能。本節(jié)將以完整的例子調(diào)試一些代碼,串講一下調(diào)試過(guò)程中經(jīng)常用到的主要功能。
1. 準(zhǔn)備一個(gè)例子
下面例子是通過(guò)并發(fā)的方式從有道的網(wǎng)站獲取多個(gè)英語(yǔ)單詞的解釋?zhuān)?將以下代碼復(fù)制到項(xiàng)目中的文件中, 比如創(chuàng)建一個(gè)文件 debug_demo.py
import requests
import re
from concurrent.futures import ThreadPoolExecutor
import time
def download_html(word):
time.sleep(5)
output = []
headers = {
'User-Agent': 'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) '
'Chrome / 72.0.3626.121Safari / 537.36'
}
url = 'http://dict.youdao.com/w/eng/{}/'.format(word)
try:
r = requests.get(url, headers=headers)
if r.status_code == 200:
pattern = re.compile(' <span class="keyword">(.*?)</span>.*?<span class="pronounce">(.*?)\n.'
'*?<span class="phonetic">(.*?)</span>.*?<span class="pronounce">(.*?)\n.*?'
'<span class="phonetic">(.*?)</span>.*?<div class="trans-container">.*?<ul>.*?'
'<li>(.*?)</li>.*?<li>(.*?)</li>', re.S)
word = re.findall(pattern, r.text)
print(word)
output.append(word)
except Exception as e:
pass
return output
if __name__ == '__main__':
text = input("請(qǐng)輸入要查詢(xún)的單詞,中間用逗號(hào)隔開(kāi):")
start = time.time()
words = text.split(',')
pool = ThreadPoolExecutor(4)
threads = [pool.submit(download_html, word) for word in words]
for i in threads:
print(i.result)
end = time.time()
print(end - start)
2. 設(shè)置斷點(diǎn)
在調(diào)試之前通常需要設(shè)置斷點(diǎn),斷點(diǎn)可以設(shè)置在循環(huán)或者條件判斷的表達(dá)式處或者程序的關(guān)鍵點(diǎn)。最為直接的方法是雙擊代碼編輯處左側(cè)邊緣,可以看到出現(xiàn)紅色的小圓點(diǎn)。
3. 啟動(dòng)調(diào)試器
PyCharm 允許以多種方式啟動(dòng)調(diào)試器會(huì)話。我們選擇在編輯器點(diǎn)擊右鍵, 在上下文菜單選擇 Debug ‘debug_demo’。
調(diào)試器啟動(dòng),顯示 Debug 工具窗口的 Consoel 選項(xiàng)卡,要求輸入想查詢(xún)的單詞:
按要求輸入單詞后回車(chē),然后調(diào)試器在第一個(gè)斷點(diǎn)掛起程序,尚未執(zhí)行帶斷點(diǎn)的行變?yōu)樗{(lán)色:
4. 單步調(diào)試
如果使用步進(jìn)工具欄按鈕 Step over,將移動(dòng)到下一行。如果單擊 Step into 按鈕,您將看到在行 ThreadPoolExecutor(3) 進(jìn)入文件 thread.py, 在 thread.py 中可繼續(xù)單擊 Step over 進(jìn)入下一步,然后單擊 Step out 回到主程序。除此以外,可以點(diǎn)擊 Debug 工具欄上的 Rusume Program (F9), 會(huì)直接移到下一個(gè)斷點(diǎn)。
Tips: 如果要專(zhuān)注于自己的代碼,請(qǐng)使用 Step into my code 單步執(zhí)行 按鈕, 可避免進(jìn)入系統(tǒng)庫(kù)類(lèi)。
5. 查看變量
可以在 Debug 工具欄中的 Variable 查看變量,如果要?jiǎng)討B(tài)的監(jiān)測(cè)某個(gè)變量可以把變量加到 Watches 欄中。當(dāng)調(diào)試進(jìn)行到該變量所在的語(yǔ)句時(shí),在該窗口中可以直接看到該變量的具體值。
在 Watches 選項(xiàng)卡中點(diǎn)擊 + 按鈕,然后鍵入要監(jiān)測(cè)的變量的名稱(chēng),代碼自動(dòng)補(bǔ)全是可用的。
Tips:如果 Watches 選項(xiàng)卡沒(méi)顯示,只需單擊 Variable 選項(xiàng)卡工具欄上的 Show watches in varaibles tab。
可能會(huì)看到一個(gè)錯(cuò)誤,這意味著變量尚未定義:
‘’
但是,當(dāng)程序執(zhí)行繼續(xù)到定義變量的范圍時(shí),就會(huì)獲取到相應(yīng)的值:
6. 子線程調(diào)試
上面的例子是多線程程序,使用 ThreadPoolExecutor 同時(shí)起 3 個(gè)線程, submit() 提交任務(wù)到線程池不是阻塞的,而是立即返回。當(dāng)主線程啟動(dòng)了子線程后,會(huì)在多線程窗口看到系統(tǒng)自動(dòng)創(chuàng)建的線程名。
當(dāng)調(diào)試進(jìn)入到各個(gè)線程的子程序時(shí),F(xiàn)rame 會(huì)自動(dòng)切換到其所對(duì)應(yīng)的 frame,相應(yīng)的變量欄中也會(huì)顯示與該過(guò)程對(duì)應(yīng)的相關(guān)變量, 使用 setp in,step over 便可以在各自的子線程進(jìn)行調(diào)試了。
7. 計(jì)算表達(dá)式
我們隨時(shí)可以計(jì)算任何表達(dá)式, 單擊"Evaluate Expression…“按鈕,然后在打開(kāi)的對(duì)話框中輸入表達(dá)式,然后單擊"Evaluate”。
8. 小結(jié)
本節(jié)例子基本覆蓋了 Python 調(diào)試過(guò)程中涉及的一些主要功能,更多細(xì)節(jié)還需要在不斷實(shí)踐中逐漸加深體會(huì)與理解,最終可以高效的調(diào)試代碼與解決遇到的問(wèn)題。