调试 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 调试过程中涉及的一些主要功能,更多细节还需要在不断实践中逐渐加深体会与理解,最终可以高效的调试代码与解决遇到的问题。

图片描述