调试 Python 程序实例
前面几小节介绍了调试 Python 程序如何调置断点、如何启动一个调试器,以及查看变量值等功能。本节将以完整的例子调试一些代码,串讲一下调试过程中经常用到的主要功能。
1. 准备一个例子
下面例子是通过并发的方式从有道的网站获取多个英语单词的解释, 将以下代码复制到项目中的文件中, 比如创建一个文件 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("请输入要查询的单词,中间用逗号隔开:")
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. 设置断点
在调试之前通常需要设置断点,断点可以设置在循环或者条件判断的表达式处或者程序的关键点。最为直接的方法是双击代码编辑处左侧边缘,可以看到出现红色的小圆点。
3. 启动调试器
PyCharm 允许以多种方式启动调试器会话。我们选择在编辑器点击右键, 在上下文菜单选择 Debug ‘debug_demo’。
调试器启动,显示 Debug 工具窗口的 Consoel 选项卡,要求输入想查询的单词:
按要求输入单词后回车,然后调试器在第一个断点挂起程序,尚未执行带断点的行变为蓝色:
4. 单步调试
如果使用步进工具栏按钮 Step over,将移动到下一行。如果单击 Step into 按钮,您将看到在行 ThreadPoolExecutor(3) 进入文件 thread.py, 在 thread.py 中可继续单击 Step over 进入下一步,然后单击 Step out 回到主程序。除此以外,可以点击 Debug 工具栏上的 Rusume Program (F9), 会直接移到下一个断点。
Tips: 如果要专注于自己的代码,请使用 Step into my code 单步执行 按钮, 可避免进入系统库类。
5. 查看变量
可以在 Debug 工具栏中的 Variable 查看变量,如果要动态的监测某个变量可以把变量加到 Watches 栏中。当调试进行到该变量所在的语句时,在该窗口中可以直接看到该变量的具体值。
在 Watches 选项卡中点击 + 按钮,然后键入要监测的变量的名称,代码自动补全是可用的。
Tips:如果 Watches 选项卡没显示,只需单击 Variable 选项卡工具栏上的 Show watches in varaibles tab。
可能会看到一个错误,这意味着变量尚未定义:
‘’
但是,当程序执行继续到定义变量的范围时,就会获取到相应的值:
6. 子线程调试
上面的例子是多线程程序,使用 ThreadPoolExecutor 同时起 3 个线程, submit() 提交任务到线程池不是阻塞的,而是立即返回。当主线程启动了子线程后,会在多线程窗口看到系统自动创建的线程名。
当调试进入到各个线程的子程序时,Frame 会自动切换到其所对应的 frame,相应的变量栏中也会显示与该过程对应的相关变量, 使用 setp in,step over 便可以在各自的子线程进行调试了。
7. 计算表达式
我们随时可以计算任何表达式, 单击"Evaluate Expression…“按钮,然后在打开的对话框中输入表达式,然后单击"Evaluate”。
8. 小结
本节例子基本覆盖了 Python 调试过程中涉及的一些主要功能,更多细节还需要在不断实践中逐渐加深体会与理解,最终可以高效的调试代码与解决遇到的问题。