修复问题并重新运行程序

发布时间:2025-06-24 20:28:53  作者:北方职教升学中心  阅读量:726


具体来说,在处理某些输入文件时,程序抛出了 ValueError或者 TypeError,导致无法继续处理。

3. Python pdb 综合案例

1. 案例背景

假设你正在开发一个复杂的数据处理程序,其中包括多个函数、

5. 总结

使用 post_mortem调试,开发者能够:

  • 在程序崩溃后迅速进入调试状态,分析错误发生的地方。

    b         # 查看所有断点
  • 删除断点:可以通过 cl命令来删除某个断点,或者删除所有断点。

  • 逐步执行:通过单步调试逐行执行代码,确保程序按照预期的逻辑执行。
  • 定位并修复错误:找出根本原因后,进行代码修改,增加异常处理或者数据验证,防止程序崩溃。

  • 5. 完整的使用过程

    步骤 1:在代码中插入 pdb.set_trace()

    我们决定在 process_user_data函数和 calculate_total_expenses函数中的关键位置插入 pdb.set_trace(),以便在程序执行到这些位置时暂停并进入调试模式。

    2.2 使用 pdbpytest结合

    pytest是一个功能强大的 Python 测试框架,广泛用于编写和运行自动化测试。

  • 修复问题并重新运行程序。通过这种方式,可以:

    • 暂停并调试测试失败的代码:遇到失败的测试时,使用 pdb进入调试器,实时检查变量状态、
    • 查看变量:使用 p命令打印变量,尤其是 expense["amount"]user["expenses"]等变量的值,检查是否有无效数据或数据结构问题。
    • 调试生产环境中的错误:有时程序错误只在特定环境中(如生产环境)才出现,这时可以利用 post_mortem对异常进行快速分析。单步执行、

      示例代码:
      importpdbimporttracebackimportlogging# 设置日志记录logging.basicConfig(filename='error_log.txt',level=logging.ERROR)defdivide(x,y):returnx /ydefmain():num1 =10num2 =0try:result =divide(num1,num2)exceptException ase:# 捕获异常,记录日志logging.error("An error occurred: %s",e,exc_info=True)# 进入 post_mortem 调试traceback.print_exc()pdb.post_mortem()if__name__ =="__main__":main()

      在这种方式下,当程序崩溃时,错误信息会被记录到日志文件中,你还可以通过 pdb调试器分析错误发生的具体位置。pdb是 Python 标准库的一部分,可以用来调试交互式脚本,也可以用于命令行下的调试。

    • c(continue):继续执行程序,直到遇到下一个断点。

      (Pdb)n

      检查 user["expenses"]的数据结构是否正确:

      (Pdb)p user["expenses"]

      步骤 5:修复错误

      在分析后,你发现某些数据中的 amount字段为空或者格式错误。

      步骤 5:退出调试器并修复错误

      知道错误的根本原因后,我们决定在代码中加入检查,避免除零错误。我们可以添加条件判断,确保除数 y不为零。

      修改后的代码如下:

      importpdbdefdivide(x,y):ify ==0:print("Error: Cannot divide by zero!")returnNone# 或者返回其他合适的值result =x /y    returnresultdefmain():num1 =10num2 =0# 故意设置为0,模拟错误result =divide(num1,num2)ifresult isnotNone:print(f"The result is: {result}")else:print("Division failed.")if__name__ =="__main__":main()
      步骤 6:重新运行程序

      现在,我们重新运行程序,并确保程序能够正确处理除零的情况:

      $ python my_program.pyError: Cannot divide by zero!Division failed.

      程序现在没有再抛出错误,而是打印了错误信息,提示用户不能进行除以零的操作。

    • 打印变量:在调试时,打印变量值,确保它们在执行过程中保持预期的状态。

      • 设置断点:使用 b命令可以设置断点。在这个案例中,我们通过插入断点、

where      # 查看调用栈
  • 查看栈帧:可以使用 u(up)和 d(down)命令在调用栈中向上或向下移动。

    1.5. pdb综合使用案例

    案例背景

    假设我们正在开发一个简单的 Python 程序,目的是计算两个数的商。

  • 多线程调试:如果程序中存在并发执行的情况,post_mortem调试可以帮助追踪并发代码崩溃时的执行状态。

    n         # 执行当前行并停在下一行
  • 进入函数(step):使用 s命令进入当前行的函数调用并逐步执行函数内部的代码。

    2. 复杂的案例

    假设你的程序包括多个函数和复杂的嵌套流程,以下是简化后的代码框架:

    importcsv# 模拟读取用户数据并处理defread_data(file_path):data =[]withopen(file_path,'r')asf:reader =csv.DictReader(f)forrow inreader:data.append(row)returndata# 处理每个用户的支出defprocess_user_data(user_data):user_expenses =0forexpense inuser_data:user_expenses +=float(expense["amount"])# 错误可能发生在此处,可能有无效数据returnuser_expenses# 计算总支出defcalculate_total_expenses(data):total =0foruser indata:total +=process_user_data(user["expenses"])# 错误可能发生在此处,数据结构可能不一致returntotal# 主函数defmain():try:file_path ='user_data.csv'# 输入文件路径data =read_data(file_path)# 读取数据total_expenses =calculate_total_expenses(data)# 计算总支出print(f"Total Expenses: {total_expenses}")exceptException ase:print(f"Error occurred: {e}")raisee

    问题:程序在处理某些用户的支出数据时出现了错误,具体是 ValueError: could not convert string to float或者 KeyError等。

    总结

    通过 pdb,我们能够:

    1. 设置断点,暂停程序的执行。这非常有用,尤其是在以下场景中:

      • 程序已经崩溃:如果程序已经抛出了未捕获的异常,使用 post_mortem可以让你在异常发生的位置立刻进入调试器。
      • 快速定位问题:直接在错误发生时进入调试模式,不需要重写测试代码。

        p my_variable + 10# 打印 my_variable 加 10 的结果

      1.4.5. 查看代码(List)

      查看当前执行位置附近的代码可以帮助你了解程序的执行上下文。

      常用命令

      • n(next):单步执行,跳到下一行。

        (Pdb)w
      • 调试嵌套函数:如果程序崩溃发生在嵌套函数中,你可以使用 ud命令来上下移动栈帧,查看不同函数中的状态。你决定在代码中增加数据验证,防止程序因无效数据而崩溃。

        2. 为什么使用 post_mortem调试?

        在正常的调试过程中,调试器通常会在代码运行时被显式地启动,例如通过在代码中插入 pdb.set_trace()来暂停执行。

        3. 调试技巧

        结合 pdbpytest进行调试时,以下技巧可以帮助你更高效地分析和解决问题:

        • 使用 p打印变量:在调试过程中,你可以使用 p命令打印变量的值。

          程序代码如下:

          defdivide(x,y):result =x /y    returnresultdefmain():num1 =10num2 =0# 这里故意设置为0以模拟除零错误result =divide(num1,num2)print(f"The result is: {result}")if__name__ =="__main__":main()

          该程序显然会抛出一个除以零的错误(ZeroDivisionError),但我们不知道具体问题出在哪里。pytest提供了与 pdb的无缝集成,使得在单元测试执行过程中,遇到错误时可以直接进入 pdb调试器,进行逐步调试。这通常通过 pdb提供的 post_mortem()函数来实现。

          cl 12# 删除第 12 行的断点cl         # 删除所有断点

        1.4.2. 继续执行(Continue)

        当程序暂停时,你可以使用 c命令继续执行,直到下一个断点或程序结束。

        • 查看当前行附近的代码:使用 l命令列出当前行前后的代码。

        • 检查变量的值,发现了程序中的问题。文件操作等。

          示例代码:
          # test_calculator.pydefadd(x,y):returnx +ydeftest_add():result =add(1,1)assertresult ==3# 故意让测试失败

          运行 pytest并使用 --pdb选项:

          $ pytest --pdbtest_calculator.py

          当测试失败时,pytest会自动进入 pdb调试模式:

          ==================================== FAILURES =====================================___________________________ test_add _____________________________________________    def test_add():        result = add(1, 1)>       assert result == 3E       assert 2 == 3test_calculator.py:6: AssertionError> /path/to/test_calculator.py(6)test_add()-> assert result == 3(Pdb)

          在调试模式下,你可以查看 result的值,找出问题所在:

          (Pdb)p result2

          你可以继续调试,查看函数内部的计算过程,分析为什么 add(1, 1)返回了 2,而不是 3

        • 使用 p命令打印变量: 在调试过程中,随时使用 p打印变量,检查数据流和结构:

          (Pdb)p expense["amount"]
        • 查看堆栈信息: 使用 wherew命令查看调用栈,理解程序的执行路径:

          (Pdb)where
        • 条件断点: 使用 b设置条件断点,仅在特定条件下暂停程序:

          (Pdb)b 15, expense["amount"]>100
        • 跳过当前循环: 如果你不想调试循环中的每一项,可以使用 n来跳过当前循环,直到到达有用的代码。

          (Pdb)u  # 向上查看上一级栈帧(Pdb)d  # 向下查看下一级栈帧
        4. 总结

        pdbpytest结合使用是一个非常强大的调试方法,可以帮助你在自动化测试过程中快速定位和解决问题。

        importpdbimportcsvdefread_data(file_path):data =[]withopen(file_path,'r')asf:reader =csv.DictReader(f)forrow inreader:data.append(row)returndatadefprocess_user_data(user_data):user_expenses =0forexpense inuser_data:pdb.set_trace()# 设置断点,查看每一项支出user_expenses +=float(expense["amount"])# 错误可能发生在此处returnuser_expensesdefcalculate_total_expenses(data):total =0foruser indata:pdb.set_trace()# 设置断点,查看每个用户的数据total +=process_user_data(user["expenses"])# 错误可能发生在此处returntotaldefmain():try:file_path ='user_data.csv'data =read_data(file_path)# 读取数据total_expenses =calculate_total_expenses(data)# 计算总支出print(f"Total Expenses: {total_expenses}")exceptException ase:print(f"Error occurred: {e}")raiseeif__name__ =="__main__":main()

        步骤 2:运行程序并进入调试模式

        运行程序时,它将在每个调用 pdb.set_trace()处暂停,并进入调试模式:

        $ python my_program.py
        > /path/to/your_program.py(12)process_user_data()-> user_expenses += float(expense["amount"])(Pdb)

        步骤 3:检查变量并分析问题

        在调试器中,使用 p命令检查变量值,查看是否有无效数据或格式问题。

      • 单步执行:通过单步执行,观察每一步的执行情况,特别是变量 xy的值。
      • 在不需要重新启动程序的情况下,直接从错误点开始调试,节省时间并提高调试效率。

        3. 调试的思路

        1. 添加断点:在可能出现错误的地方设置断点,观察数据流和变量状态。

          • 查看调用栈:使用 wherew命令查看当前的调用栈。

          通过合理使用 post_mortem,你可以更高效地分析并解决程序中的问题,尤其是在面对难以重现的错误时。

          2. pdb的高级调试技巧

          2.1 使用 post_mortem调试

          post_mortem调试是一种强大的调试技术,用于在程序崩溃后分析和诊断错误,而无需重新运行程序。

          示例:

          b 12# 在第 12 行设置断点b my_func # 在函数 my_func 入口处设置断点b filename.py:15  # 在文件 filename.py 的第 15 行设置断点
        2. 查看断点:使用 b命令可以查看当前设置的所有断点。

        完整的使用过程

        步骤 1:在代码中添加断点

        我们首先导入 pdb模块,并在除法操作之前设置断点。

        u          # 向上跳到上一级栈帧d          # 向下跳到下一级栈帧

      1.4.7. 跳过某些代码(Return)

      如果你不想单步执行某些代码,可以使用 r命令跳过当前函数的剩余部分,直接执行到当前函数返回。

    4. 调试的技巧

    1. 使用 --pdb自动启动调试器: 通过运行 pytest --pdb或者在命令行中使用 python -m pdb来启动调试,这可以在错误发生时自动进入调试状态。

    2. b(break):设置一个新的断点,指定位置(行号或函数名),例如 b 15b my_function

      2.2 在测试中手动插入 pdb.set_trace()

      如果你希望在特定的测试步骤或代码位置手动设置断点,可以在测试代码中插入 pdb.set_trace()来控制调试的开始。如果当前行包含函数调用,n会跳过该函数的内部执行。

      b 12, x >10# 只有当 x > 10 时才会在第 12 行暂停
    3. 总结

      pdb提供了许多调试工具,可以帮助开发者深入分析程序执行,查找问题。例如,你可以查看 num1num2的值来确认为什么会发生除零错误:

      (Pdb)p num110(Pdb)p num20
      3.3 离开 post_mortem调试

      完成分析后,可以通过输入 q命令退出调试器并终止程序执行:

      (Pdb)q

      4. 高级用法:结合日志和异常捕获

      在实际开发中,你可能会希望在程序中结合 post_mortem调试和日志记录。

      • 退出调试器:使用 q命令可以退出调试模式并结束程序。

        (Pdb)p x10(Pdb)p y0

        通过观察,我们看到 y的值是 0,这就是导致除法错误的原因。这个选项非常方便,不需要在测试代码中手动插入 pdb.set_trace(),它会在测试运行失败时自动进入调试模式。这段程序用于处理一些用户上传的 CSV 文件,计算每个用户的总支出,并输出结果。

        1. Python pdb 基础

        1.1. pdb 是什么?

        pdb(Python Debugger)是 Python 自带的调试工具,它允许开发者在 Python 程序运行时设置断点,逐步执行代码,检查变量值,并帮助找出程序中的错误。

        由于数据量大,错误并非每次都能复现,并且错误的具体位置在多个函数间存在嵌套,单纯使用日志或者打印信息来检查状态非常困难。这样,你可以在程序崩溃时记录详细的错误信息,同时又能通过 post_mortem进入调试器进行分析。

        步骤 4:单步执行

        我们可以使用 n命令单步执行代码,观察代码执行的每一行。IndexError)时非常有用,特别是当异常发生时程序已经退出或者没有显式的调试代码时。

        $ pytest --pdb

        这时,如果其中任何一个测试失败,pytest会暂停并进入调试模式,你可以使用常用的 pdb命令查看出错位置并调试代码。

        s         # 如果当前行包含函数调用,会进入函数内部

      1.4.4. 查看变量(Print)

      在调试过程中,检查变量的值是很常见的操作。控制执行过程,你能够高效地分析和解决测试失败问题。

    pdb是一个强大的调试工具,能帮助我们在程序出现错误时,快速定位和修复问题,提升开发效率。函数名或者文件名来指定断点。

    $ pytest test_calculator.py
    > /path/to/test_calculator.py(7)test_add()-> result = add(num1, num2)(Pdb)

    你可以查看变量的状态,逐步分析问题。

  • 单步执行代码,分析问题的根本原因。

    p my_variable  # 打印变量 my_variable 的值
  • 表达式求值:可以在 p命令后输入任何合法的 Python 表达式,查看其值。

  • s(step):进入当前行中调用的函数,逐步执行函数内部的代码。

    2.3 调试测试套件中的多个测试

    如果你希望调试整个测试套件中的多个测试,可以在运行 pytest时加上 --pdb选项,然后在某个测试失败时进入调试器。

    $ python my_program.py> /path/to/your_program.py(5)divide()-> result = x / y(Pdb)
    步骤 3:检查变量值

    进入调试模式后,我们可以使用 p命令来查看变量 xy的值。结合 pdb调试器进行调试,可以极大提高调试效率。栈信息等。

    使用 pdb 的思路

    1. 添加断点:我们将在可能出错的地方(如除法操作前)添加断点。这在分析未处理异常(例如 ZeroDivisionError、使用 pdb可以在出错时暂停程序,让你深入分析失败的原因。由于数据量庞大,错误不易直接复现,且程序的执行路径较为复杂,因此传统的日志打印方法无法有效定位问题。

      在开发过程中,你遇到了一些非常棘手的错误。

      l 10,20# 查看第 10 行到第 20 行的代码
    2. 1.4.6. 调试栈(Stack Trace)

      查看调用栈有助于理解程序的执行路径和函数调用关系。

      1.4.1. 设置断点(Breakpoints)

      断点用于在特定位置暂停程序的执行,让你可以检查程序的状态。

      1.2. pdb 作用是什么?

      pdb的主要作用是帮助开发者定位和修复代码中的错误。

    3. p(print):打印指定变量的值,例如 p variable
    4. 交互式分析:在交互模式下,你可以逐步执行测试并检查代码状态,找到问题的根源。

      l         # 查看当前行周围的代码
    5. 查看指定位置的代码:可以通过指定行号来查看特定的代码。

      为什么需要 pdb?

      我们需要使用 pdb来调试这段代码,以确定:

      • 程序在哪里出错
      • 错误发生时,变量 xy的值是什么
      • 我们如何修复这个问题

      直接查看代码是不能直接揭示运行时变量的状态的,尤其是在出现异常时,因此使用调试器能够让我们在程序运行时暂停,并深入检查错误的原因。检查变量值以及增加数据验证,成功定位并解决了程序中因无效数据导致的错误。数据转换、它使得开发者能够在程序抛出异常之后,直接进入调试器,从崩溃点开始检查程序状态。

      r         # 跳出当前函数并停在返回后的第一行
    6. 1.4.8. 退出调试器(Quit)

      如果你已经完成调试,或者想停止调试并退出,可以使用 q命令退出调试器。此时,我们可以使用调试命令来检查程序状态。

      (Pdb)c  # 继续执行直到下一个断点
    7. 检查函数调用栈:使用 wherew命令查看当前的调用栈,了解当前代码的执行路径。

      • 单步执行(next):使用 n命令逐行执行代码。通过断点、

      3. 如何使用 post_mortem调试?

      3.1 捕获异常并进入 post_mortem

      pdb提供了 post_mortem()函数,可以在捕获异常时启动调试会话。

      $ python my_program.py

      总结

      通过结合 pdb调试器,你能够在复杂的程序中逐步分析问题、手动插入 pdb.set_trace()或者利用调试命令来查看变量、外部库调用、

    8. 2. 在 pytest中使用 pdb

      2.1 使用 --pdb选项自动启动调试

      pytest提供了 --pdb命令行选项,可以在测试失败时自动启动 pdb调试器。

    9. 跟踪错误:找出导致 ZeroDivisionError的原因,并进行修复。

      修改代码:

      defprocess_user_data(user_data):user_expenses =0forexpense inuser_data:ifnotexpense.get("amount")ornotexpense["amount"].replace(".","",1).isdigit():print(f"Invalid data for expense: {expense}")# 打印错误数据continueuser_expenses +=float(expense["amount"])returnuser_expenses

      步骤 6:重新运行程序

      重新运行程序,确认问题是否已经修复。pytest会自动让你进入调试状态,允许你对失败的测试进行逐步调试。你可以通过行号、

      (Pdb)n

      但在这一步我们已经知道是因为除以零导致了错误,因此我们不需要继续执行。

    10. try-except块中使用 pdb.set_trace(): 在可能出错的 try-except块中插入 pdb.set_trace(),使程序在错误发生前暂停,并进入调试器。

    11. 跟踪数据结构:如果出现 KeyErrorValueError,使用 ln等命令来查看当前代码和变量内容,理解数据结构和流程。
    12. 例子

      importpdbdefdivide(x,y):pdb.set_trace()# 在这里暂停程序returnx /yprint(divide(10,2))

      当程序执行到 pdb.set_trace()时,它会暂停,并进入 pdb调试模式,你可以使用调试命令查看和修改程序的执行状态。

    13. q(quit):退出调试器并终止程序执行。

      示例代码:
      importpdbimporttracebackdefdivide(x,y):returnx /ydefmain():num1 =10num2 =0# 故意设置为0来模拟除零错误try:result =divide(num1,num2)exceptException ase:print(f"An error occurred: {e}")# 捕获异常后进入 post_mortem 调试traceback.print_exc()# 打印详细的错误堆栈信息pdb.post_mortem()# 调用 post_mortem 进入调试器if__name__ =="__main__":main()
      3.2 执行结果

      当程序运行时,会抛出 ZeroDivisionError,并且 except块捕获到异常后,pdb.post_mortem()会启动调试器,允许我们进入崩溃点进行分析。为了调试这个问题,我们将使用 pdb

      1. 为什么将 pdbpytest结合使用?

      在进行自动化测试时,遇到测试失败的情况,直接跳过错误或查看日志信息并不足够。

    14. 结合日志记录,跟踪异常信息,从而更好地定位和修复错误。

      (Pdb)n
    15. 跳过某些代码:当你不想深入某个函数时,可以使用 s命令跳过,或者 c命令继续执行到下一个断点。

      q         # 退出调试器并终止程序
    16. 1.4.9. 设置条件断点

      有时你只希望在特定条件下暂停程序的执行,而不是每次都停在断点。在调试过程中,合理使用这些命令,能够高效地进行调试,提升代码质量。通过这种交互式调试,你可以:

      • 在程序的特定位置暂停执行
      • 单步调试代码,逐行执行
      • 检查和修改变量值
      • 跟踪函数调用栈
      • 执行 Python 表达式并查看结果

      1.3. pdb命令以及基础使用

      在调试过程中,你可以使用多种 pdb命令来控制程序的执行,以下是一些常用命令:

      启动 pdb

      在程序中需要调试的地方添加如下代码:

      importpdb;pdb.set_trace()

      这样程序会在此位置暂停,并进入交互式调试模式。

      1. 什么是 post_mortem调试?

      post_mortem调试指的是在程序崩溃(抛出异常)后,进入调试器并分析异常时的堆栈信息和程序状态。

      importpdbdefdivide(x,y):pdb.set_trace()# 设置断点,暂停执行result =x /y    returnresultdefmain():num1 =10num2 =0# 故意设置为0,模拟错误result =divide(num1,num2)print(f"The result is: {result}")if__name__ =="__main__":main()
      步骤 2:运行程序并进入调试模式

      运行程序后,程序会在 pdb.set_trace()这一行暂停,并进入调试模式。通过使用 --pdb选项、

      • 设置条件断点:使用 b命令设置条件断点,在指定的条件成立时才暂停程序。它接受一个 traceback对象,通常我们会在 try/except块中捕获异常,并将 traceback传递给 post_mortem()

        步骤 4:逐步执行代码

        使用 n命令单步执行,查看循环中的每个步骤。

        c

      1.4.3. 单步调试(Step)

      单步调试是指逐行执行代码,帮助你深入理解每一行代码的执行效果。

      (Pdb)p result
    17. 单步执行:使用 n命令单步执行代码,逐行检查程序的执行过程。

      An error occurred: division by zero> /path/to/your_program.py(6)main()-> result = divide(num1, num2)(Pdb)

      此时,我们进入了 pdb调试器,你可以查看异常发生时的堆栈信息,并对变量进行检查。因此,你决定使用 pdb来在程序出错时逐步调试,分析数据流和变量的状态。

      • 继续执行:当程序暂停后,使用 c命令可以继续执行,直到遇到下一个断点。可以使用条件断点来实现。pdb是一个强大的调试工具,能够帮助你在复杂代码中高效定位问题,提高开发效率。查看变量等操作,你可以快速定位问题并解决。检查变量并修复错误。单步执行、

        • 查看变量的值:使用 p命令打印变量的值。然而,在实际运行时,程序出现了除以零的错误。然而,post_mortem调试则不需要显式暂停程序,它能让你在程序崩溃后直接进入调试环境,回溯错误的发生位置。

          • 跳出当前函数:当你进入了一个函数,但想直接跳出并继续执行,可以使用 r命令。

            (Pdb)p expense["amount"]'100.5'# 检查金额是否正确

            如果发现某些数据项为 None或空字符串,可能会导致 ValueError。例如,查看一个函数返回值的计算过程。它允许你在程序执行时暂停,检查当前的程序状态,逐行执行代码,查看各个变量的值,以便找出潜在的问题。

          • l(list):显示当前执行位置附近的代码。

            你决定使用 pdb来进行调试,以便逐步分析问题并找到根本原因。

            这种方法可以显著提高开发效率,特别是在调试复杂的错误或难以重现的缺陷时,它让你能够迅速进入问题的根源,并采取行动。这样可以暂停程序,检查变量 xy的值。

            示例代码:
            # test_calculator.pyimportpdbdefadd(x,y):returnx +ydeftest_add():num1,num2 =1,1pdb.set_trace()# 在这里设置断点result =add(num1,num2)assertresult ==3# 故意让测试失败

            运行测试时,程序会在 pdb.set_trace()处暂停,你可以使用调试命令进行交互式分析。

            在本节中,我们将详细讨论如何在使用 pytest进行单元测试时,结合 pdb进行调试。

            1.4. pdb常见调试操作

            在使用 pdb进行调试时,以下是一些常见操作和命令,它们可以帮助你逐步分析程序,诊断和修复问题。我们需要找到代码中导致这个错误的原因,并通过调试来修复它。