python自产调试工具pdb的使用

python,自产,调试,工具,pdb,使用 · 浏览次数 : 127

小编点评

3.2 新版功能: * `>>` 标记:用于指示表达式的值发生改变时应该显示其值。 * `ll | longlist`:用于列出当前函数或帧的所有源代码。 * ``..`:用于表示当前行周围的 11 行。 * ``*`:用于表示所有形参。 * `%1, %2 等`:用于标示参数。 * `print()` 模块:用于美观地打印表达式的值。 * ``%`:用于格式化输出参数。 * ``::`:用于将别名值当做参数传递。 * ``()`:用于嵌套别名。 * ``statement`:用于执行代码片段。 * ``=`:用于设置全局变量。 * ``:`:用于设置别名。 * ``::`:用于设置别名。 * ``statement`:用于执行代码片段。 3.2 新版功能.a(rgs) 打印当前函数的参数列表。 * `p expression`:用于在当前上下文中运行 expression 并打印它的值。 * ``::`:用于将别名值当做参数传递。 3.2 新版功能.display [expression] 如果表达式的值发生改变则显示它的值,每次将停止执行当前帧。 * ``::`:用于将别名值当做参数传递。 3.2 新版功能.undisplay [expression]不再显示当前帧中的表达式。 * ``::`:用于将别名值当做参数传递。 3.2 新版功能.interact 启动一个交互式解释器(使用 code 模块),它的全局命名空间将包含当前作用域中的所有(全局和局部)名称。 * ``::`:用于将别名值当做参数传递。

正文

python自产调试工具pdb的使用

介绍

  • 调试打印在写代码的时候不可避免
  • 项目越大,调试可能花的时间会越多
  • print调试可能是最早用的,一段时间内你都会习惯这种方式
  • 一旦成了老鸟,你应该会去用IDE的debugger,功能非常强大,效率就比print上了一个台阶
  • 当然python像其他语言一样,也有自己的调试工具,pdb
  • python debugger:目的无非是为了知道程序此时的状态(在做啥,环境信息),当然它还要能暂停你的程序

调测代码

  • LeetCode 1. 两数之和 为例

  • 题目

    给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。
    
    你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
    
    你可以按任意顺序返回答案。
    
     
    
    示例 1:
    
    输入:nums = [2,7,11,15], target = 9
    输出:[0,1]
    解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
    示例 2:
    
    输入:nums = [3,2,4], target = 6
    输出:[1,2]
    示例 3:
    
    输入:nums = [3,3], target = 6
    输出:[0,1]
    
    提示: # 下面的这些不考虑
    
    2 <= nums.length <= 104
    -109 <= nums[i] <= 109
    -109 <= target <= 109
    只会存在一个有效答案
     
    
    进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
    
  • 我写个错误的代码demo_twosum.py

    class Solution:
        def twoSum(self, nums: List[int], target: int) -> List[int]:
            for i,v in enumerate(nums):
                if target-v in nums: 
                    return [i,nums.index(target-v)]
    
  • 对于以下cases,应返回[1,2],实际返回[0,0]

    输入:nums = [3,2,4], target = 6
    
  • 因为题目中有要求:数组中同一个元素在答案里不能重复出现


pdb调试

切入pdb的方式

  • 我们来看看如何用pdb来调试这段代码

    from typing import List
    class Solution:
        def twoSum(self, nums: List[int], target: int) -> List[int]:
            for i,v in enumerate(nums):
                if target-v in nums:
                    return [i,nums.index(target-v)]
    nums = [3,2,4]
    target = 6
    solution = Solution().twoSum(nums,target)
    print(solution)
    
  • 你可以执行

    python -m pdb demo_twosum.py
    
  • 执行界面大致如下

    # python -m pdb demo_twosum.py
    >  demo_twosum.py(1)<module>()
    -> from typing import List
    
  • 你还可以在代码中加breakpoint()

                if target-v in nums: 
                    breakpoint()  # 加在你要调试的代码附近 Line 7
                    return [i,nums.index(target-v)]
    
  • 运行你的代码提示如下

    D:\Python39\python.exe  demo_twosum.py
    >  demo_twosum.py(7)twoSum()
    -> return [i,nums.index(target-v)]
    (Pdb) 
    

常用的调试命令

  • 典型的操作

    D:\pythonProject\外部公开课\当前公开课>python -m pdb demo_twosum.py
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(1)<module>()
    -> from typing import List
    (Pdb) b 5
    Breakpoint 1 at d:\pythonproject\外部公开课\当前公开课\demo_twosum.py:5
    (Pdb) c
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5)twoSum()
    -> if target-v in nums:
    
    (Pdb) w
      d:\python39\lib\bdb.py(580)run()
    -> exec(cmd, globals, locals)
      <string>(1)<module>()
      d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(9)<module>()
    -> solution = Solution().twoSum(nums,target)
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5)twoSum()
    -> if target-v in nums:
    
    
    (Pdb) p i
    0
    (Pdb) p target
    6
    (Pdb) p v
    3
    (Pdb) r
    --Return--
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(6)twoSum()->[0, 0]
    -> return [i,nums.index(target-v)]
    (Pdb) l
      1     from typing import List
      2     class Solution:
      3         def twoSum(self, nums: List[int], target: int) -> List[int]:
      4             for i,v in enumerate(nums):
      5 B               if target-v in nums:
      6  ->                 return [i,nums.index(target-v)]
      7     nums = [3,2,4]
      8     target = 6
      9     solution = Solution().twoSum(nums,target)
     10     print(solution)
    [EOF]
    
    (Pdb) p  [i,nums.index(target-v)]
    [0, 0]
    (Pdb) q
    
  • 所以,这是在做啥?

  • 逐行解释

  • 第1行

    D:\pythonProject>python -m pdb demo_twosum.py
    > d:\pythonproject\demo_twosum.py(1)<module>()
    -> from typing import List
    
    • 当前文件绝对路径 d:\pythonproject\demo_twosum.py

    • 处于第一行 (1)

    • 是个module <module>()

    • 第一行的内容 -> from typing import List # 这是你即将运行的代码

    • 参考上面breakpoint()的方式,你会发现显示的是你打了断点下面的那一行代码。

      -> return [i,nums.index(target-v)]
      
  • 第2行

    (Pdb) b 5
    Breakpoint 1 at d:\pythonproject\外部公开课\当前公开课\demo_twosum.py:5
    
    • 在第5行设置一个断点
    • 如果就写一个b(break),那就是列出当前所有的断点
    • 对应的clear 断点编号就可以取消该断点
  • 第3行

    (Pdb) c
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5)twoSum()
    -> if target-v in nums:
    
    • 运行到设置的断点处
  • 第4行

    (Pdb) w
      d:\python39\lib\bdb.py(580)run()
    -> exec(cmd, globals, locals)
      <string>(1)<module>()
      d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(9)<module>()
    -> solution = Solution().twoSum(nums,target)
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(5)twoSum()
    -> if target-v in nums:
    
    
    • 查看当前的调用栈
    • 是不是有点像堆栈的打印
  • 第5行

    (Pdb) p i
    0
    (Pdb) p target
    6
    (Pdb) p v
    3
    
    • 打印当前的变量

    • pp则是类似于pprint,美化输出

    • 注意p可以打印任意有效的python表达式

    • 值得一提的是,你甚至可以改变此时的程序(print做不到了)

      # 比如 任意的python 语句
      v = 3 
      
  • 第6行

    (Pdb) r
    --Return--
    > d:\pythonproject\外部公开课\当前公开课\demo_twosum.py(6)twoSum()->[0, 0]
    -> return [i,nums.index(target-v)]
    
    • 运行到返回
  • 第7行

    (Pdb) l
    
    • 打印当前位置前后11行代码
    • 再输入一个l,则往下再翻11行
    • l.可以列出当前行周围的 11 行
  • 第8行:打印[i,nums.index(target-v)],发现是[0,0]

  • 第9行:q,退出

附录 pdb命令全解

  • h(elp) [command]

    不带参数时,显示可用的命令列表。参数为 command 时,打印有关该命令的帮助。help pdb 显示完整文档(即 pdb 模块的文档字符串)。由于 command 参数必须是标识符,因此要获取 ! 的帮助必须输入 help exec

  • w(here)

    打印堆栈回溯,最新一帧在底部。有一个箭头指向当前帧,该帧决定了大多数命令的上下文。

  • d(own) [count]

    在堆栈回溯中,将当前帧向下移动 count 级(默认为 1 级,移向更新的帧)。

  • u(p) [count]

    在堆栈回溯中,将当前帧向上移动 count 级(默认为 1 级,移向更老的帧)。

  • b(reak) [([filename:]lineno | function) [, condition]]

    如果带有 lineno 参数,则在当前文件相应行处设置一个断点。如果带有 function 参数,则在该函数的第一条可执行语句处设置一个断点。行号可以加上文件名和冒号作为前缀,以在另一个文件(可能是尚未加载的文件)中设置一个断点。另一个文件将在 sys.path 范围内搜索。请注意,每个断点都分配有一个编号,其他所有断点命令都引用该编号。如果第二个参数存在,它应该是一个表达式,且它的计算值为 true 时断点才起作用。如果不带参数执行,将列出所有中断,包括每个断点、命中该断点的次数、当前的忽略次数以及关联的条件(如果有)。

  • tbreak [([filename:]lineno | function) [, condition]]

    临时断点,在第一次命中时会自动删除。它的参数与 break 相同。

  • cl(ear) [filename:lineno | bpnumber ...]

    如果参数是 filename:lineno,则清除此行上的所有断点。如果参数是空格分隔的断点编号列表,则清除这些断点。如果不带参数,则清除所有断点(但会先提示确认)。

  • disable [bpnumber ...]

    禁用断点,断点以空格分隔的断点编号列表给出。禁用断点表示它不会导致程序停止执行,但是与清除断点不同,禁用的断点将保留在断点列表中并且可以(重新)启用。

  • enable [bpnumber ...]

    启用指定的断点。

  • ignore bpnumber [count]

    为指定的断点编号设置忽略次数。如果省略 count,则忽略次数将设置为 0。忽略次数为 0 时断点将变为活动状态。如果为非零值,在每次达到断点,且断点未禁用,且关联条件计算值为 true 的情况下,该忽略次数会递减。

  • condition bpnumber [condition]

    为断点设置一个新 condition,它是一个表达式,且它的计算值为 true 时断点才起作用。如果没有给出 condition,则删除现有条件,也就是将断点设为无条件。

  • commands [bpnumber]

    为编号是 bpnumber 的断点指定一系列命令。命令内容将显示在后续的几行中。输入仅包含 end 的行来结束命令列表。举个例子:(Pdb) commands 1 (com) p some_variable (com) end (Pdb) 要删除断点上的所有命令,请输入 commands 并立即以 end 结尾,也就是不指定任何命令。如果不带 bpnumber 参数,commands 作用于最后一个被设置的断点。可以为断点指定命令来重新启动程序。只需使用 continuestep 命令或其他可以继续运行程序的命令。如果指定了某个继续运行程序的命令(目前包括 continue, step, next, return, jump, quit 及它们的缩写)将终止命令列表(就像该命令后紧跟着 end)。因为在任何时候继续运行下去(即使是简单的 next 或 step),都可能会遇到另一个断点,该断点可能具有自己的命令列表,这导致要执行的列表含糊不清。如果在命令列表中加入 'silent' 命令,那么在该断点处停下时就不会打印常规信息。如果希望断点打印特定信息后继续运行,这可能是理想的。如果没有其他命令来打印一些信息,则看不到已达到断点的迹象。

  • s(tep)

    运行当前行,在第一个可以停止的位置(在被调用的函数内部或在当前函数的下一行)停下。

  • n(ext)

    继续运行,直到运行到当前函数的下一行,或当前函数返回为止。( nextstep 之间的区别在于,step 进入被调用函数内部并停止,而 next (几乎)全速运行被调用函数,仅在当前函数的下一行停止。)

  • unt(il) [lineno]

    如果不带参数,则继续运行,直到行号比当前行大时停止。如果带有行号,则继续运行,直到行号大于或等于该行号时停止。在这两种情况下,当前帧返回时也将停止。在 3.2 版更改: 允许明确给定行号。

  • r(eturn)

    继续运行,直到当前函数返回。

  • c(ont(inue))

    继续运行,仅在遇到断点时停止。

  • j(ump) lineno

    设置即将运行的下一行。仅可用于堆栈最底部的帧。它可以往回跳来再次运行代码,也可以往前跳来跳过不想运行的代码。需要注意的是,不是所有的跳转都是允许的 -- 例如,不能跳转到 for 循环的中间或跳出 finally 子句。

  • l(ist) [first[, last]]

    列出当前文件的源代码。如果不带参数,则列出当前行周围的 11 行,或继续前一个列表。如果用 . 作为参数,则列出当前行周围的 11 行。如果带有一个参数,则列出那一行周围的 11 行。如果带有两个参数,则列出所给的范围中的代码;如果第二个参数小于第一个参数,则将其解释为列出行数的计数。当前帧中的当前行用 -> 标记。如果正在调试异常,且最早抛出或传递该异常的行不是当前行,则那一行用 >> 标记。3.2 新版功能: >> 标记。

  • ll | longlist

    列出当前函数或帧的所有源代码。相关行的标记与 list 相同。3.2 新版功能.

  • a(rgs)

    打印当前函数的参数列表。

  • p expression

    在当前上下文中运行 expression 并打印它的值。注解 print() 也可以使用,但它不是一个调试器命令 --- 它执行 Python print() 函数。

  • pp expression

    p 命令类似,但表达式的值使用 pprint 模块美观地打印。

  • whatis expression

    打印 expression 的类型。

  • source expression

    尝试获取给定对象的源代码并显示出来。3.2 新版功能.

  • display [expression]

    如果表达式的值发生改变则显示它的值,每次将停止执行当前帧。不带表达式则列出当前帧的所有显示表达式。3.2 新版功能.

  • undisplay [expression]

    不再显示当前帧中的表达式。 不带表达式则清除当前帧的所有显示表达式。3.2 新版功能.

  • interact

    启动一个交互式解释器(使用 code 模块),它的全局命名空间将包含当前作用域中的所有(全局和局部)名称。3.2 新版功能.

  • alias [name [command]]

    创建一个标识为 name 的别名来执行 command。 执行的命令 不可 加上引号。 可替换形参可通过 %1, %2 等来标示,而 %* 会被所有形参所替换。 如果没有给出命令,则会显示 name 的当前别名。 如果没有给出参数,则会列出所有别名。别名允许嵌套并可包含能在 pdb 提示符下合法输入的任何内容。 请注意内部 pdb 命令 可以 被别名所覆盖。 这样的命令将被隐藏直到别名被移除。 别名会递归地应用到命令行的第一个单词;行内的其他单词不会受影响。作为示例,这里列出了两个有用的别名(特别适合放在 .pdbrc 文件中):# Print instance variables (usage "pi classInst") alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k]) # Print instance variables in self alias ps pi self

  • unalias name

    删除指定的别名。

  • ! statement

    在当前堆栈帧的上下文中执行 (单行) statement。 感叹号可以被省略,除非语句的第一个单词与调试器命令重名。 要设置全局变量,你可以在同一行上为赋值命令添加前缀的 global 语句,例如:(Pdb) global list_options; list_options = ['-l'] (Pdb)

  • run [args ...]

  • restart [args ...]

    重启被调试的 Python 程序。 如果提供了参数,它会用 shlex 来拆分且拆分结果将被用作新的 sys.argv。 历史、中断点、动作和调试器选项将被保留。 restartrun 的一个别名。

  • q(uit)

    退出调试器。 被执行的程序将被中止。

  • debug code

    进入一个对代码参数执行步进的递归调试器(该参数是在当前环境中执行的任意表达式或语句)。

  • retval

    打印函数最后一次返回的返回值。

与python自产调试工具pdb的使用相似的内容:

python自产调试工具pdb的使用

python自产调试工具pdb的使用 介绍 调试打印在写代码的时候不可避免 项目越大,调试可能花的时间会越多 print调试可能是最早用的,一段时间内你都会习惯这种方式 一旦成了老鸟,你应该会去用IDE的debugger,功能非常强大,效率就比print上了一个台阶 当然python像其他语言一样,

从基础到高级应用,详解用Python实现容器化和微服务架构

本文分享自华为云社区《Python微服务与容器化实践详解【从基础到高级应用】》,作者: 柠檬味拥抱。 Python中的容器化和微服务架构实践 在现代软件开发中,容器化和微服务架构已经成为主流。容器化技术使得应用程序可以在任何环境中一致运行,而微服务架构通过将应用拆分成多个独立的服务,从而提升了系统的

基于Python和TensorFlow实现BERT模型应用

本文分享自华为云社区《使用Python实现深度学习模型:BERT模型教程》,作者: Echo_Wish。 BERT(Bidirectional Encoder Representations from Transformers)是Google提出的一种用于自然语言处理(NLP)的预训练模型。BERT

一文带你搞清楚Python的多线程和多进程

本文分享自华为云社区《Python中的多线程与多进程编程大全【python指南】》,作者:柠檬味拥抱。 Python作为一种高级编程语言,提供了多种并发编程的方式,其中多线程与多进程是最常见的两种方式之一。在本文中,我们将探讨Python中多线程与多进程的概念、区别以及如何使用线程池与进程池来提高并

如何使用Python和Plotly绘制3D图形

本文分享自华为云社区《Plotly绘制3D图形》 ,作者:柠檬味拥抱。 在数据可视化领域,三维图形是一种强大的工具,可以展示数据之间的复杂关系和结构。Python语言拥有丰富的数据可视化库,其中Plotly是一款流行的工具,提供了绘制高质量三维图形的功能。本文将介绍如何使用Python和Plotly

地理数据可视化的神奇组合:Python和Geopandas

本文分享自华为云社区《Python与Geopandas:地理数据可视化与分析指南》,作者:柠檬味拥抱。 地理数据可视化在许多领域都是至关重要的,无论是研究地理空间分布、城市规划、环境保护还是商业决策。Python语言以其强大的数据处理和可视化库而闻名,而Geopandas作为其地理信息系统(GIS)

Python函数与模块的精髓与高级特性

本文分享自华为云社区《Python函数与模块的精髓与高级特性》,作者:柠檬味拥抱。 Python 是一种功能强大的编程语言,拥有丰富的函数和模块,使得开发者能够轻松地构建复杂的应用程序。本文将介绍 Python 中函数和模块的基本使用方法,并提供一些代码实例。 1. 函数的定义与调用 函数是一段完成

详解Python 中可视化数据分析工作流程

本文分享自华为云社区《Python 可视化数据分析从数据获取到洞见发现的全面指南》,作者:柠檬味拥抱。 在数据科学和分析的领域中,可视化是一种强大的工具,能够帮助我们理解数据、发现模式,并得出洞见。Python 提供了丰富的库和工具,使得可视化数据分析工作流程变得高效而灵活。本文将介绍 Python

11个Python循环技巧

本文分享自华为云社区《Python中的循环技巧指南》,作者:柠檬味拥抱。 当我们处理数据时,有时候需要创建多个列表以存储不同类型或不同条件下的数据。在Python中,我们可以利用循环来快速、高效地创建这些列表。本文将介绍如何使用循环在Python中创建多个列表,并提供代码实例。 python用循环新

实践探讨Python如何进行异常处理与日志记录

本文分享自华为云社区《Python异常处理与日志记录构建稳健可靠的应用》,作者:柠檬味拥抱。 异常处理和日志记录是编写可靠且易于维护的软件应用程序中至关重要的组成部分。Python提供了强大的异常处理机制和灵活的日志记录功能,使开发人员能够更轻松地管理代码中的错误和跟踪应用程序的执行过程。在本文中,