Python里三个好用的调试神器

调试是开发过程中不可避免的一个环节,在Python中我们使用print、logging、assert等方法进行调试既简单又实用,但毕竟有其局限性。今天这篇文章为大家带来三个工具,其中有Python的内置模块也有第三方库,它们提供了调试代码所需的大部分常用功能,将极大的提升我们的开发和bug排除效率。

1.PDB

pdb是Python中的一个内置模块,启用pdb后可以对代码进行断点设置和跟踪调试。为了演示方便,我们准备一个样例程序pdb_test.py:

def countnumber(number):
  for i in range(number):
    print(i)

if __name__ == ‘__main__‘:
  countnumber(10)

之后在终端中输入python -m pdb pdb_test.py命令,进入pdb的调试模式:

这时我们就可以通过各种命令控制代码执行或者查看当前变量,例如l可以查看所有代码,n是执行下一步代码,p可以查看当前变量等等,需要注意的是命令n只会执行主程序中的代码,如果想要单步执行子函数中的代码,需要使用s指令,调试效果如下:

这时我们就可以通过各种命令控制代码执行或者查看当前变量,例如l可以查看所有代码,n是执行下一步代码,p可以查看当前变量等等,需要注意的是命令n只会执行主程序中的代码,如果想要单步执行子函数中的代码,需要使用s指令,调试效果如下:

可以看到,通过s指令(如果只想在主函数中单步执行可以使用n)和p指令,我们控制程序单步运行并实时查看了相关变量。但是单步执行毕竟是一种效率非常低下的调试方式,尤其当代码量比较大的时候更是噩梦,这时就需要用到pdb的set_trace()方法,我们对样例程序pdb_test.py做一点修改:

‘‘‘
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
‘‘‘
import pdb
def countnumber(number):
  for i in range(number):
    print(i)
    pdb.set_trace()

if __name__ == ‘__main__‘:
  countnumber(10)

pdb.set_trace()的作用就是在代码中设置断点,在pdb调试模式下,使用c命令就会直接跳转到下一个断点位置,如果之后没有其他断点就会执行完全部代码,调试效果如下:

除了上面提到的几个指令以外,pdb还有其他一些比较常用的命令(见下表),综合使用基本能够满足日常的调试需求。

2.Better-exceptions

better-exceptions是一个Python第三方库,作者对他的定义是“使异常信息更加美观和详尽”。在正式使用之前先说下这个库的安装:

第一步,使用pip install better_exceptions安装better-exceptions库;
第二步,使用export BETTER_EXCEPTIONS=1(Linux / OSX)或setx BETTER_EXCEPTIONS 1(Windows)设置环境变量。

现在就可以正常使用better-exceptions进行调试了,为了演示效果更加明显,我们对上文中的代码稍作修改作为本次的样例程序better_test.py:

def divisionnumber(number, div):
  for i in range(div):
    print(number / i)

if __name__ == ‘__main__‘:
  divisionnumber(10, 10)

很明显,上面这段代码在执行过程中会因为分母为0而抛出异常,现在我们执行python better_test.py,看看启用了better-exceptions后的异常信息是什么样子的:

从上面这幅图可以看出better-exceptions对异常信息的修改主要体现在两个方面:

一是对产生异常的代码进行了颜色标注;

二是对产生异常的代码中的相关变量值进行了输出(包括函数等对象);

这样一来,很多时候我们只需要根据better-exceptions输出的辅助信息就能判断产生异常的位置和原因,而不必像以前一样再次查看源代码并观察运行结果,正如作者所说:Pretty and more helpful。
但是,过多的信息输出也会有问题,那就是当代码层级结构比较复杂的时候,better-exceptions输出的辅助信息可能会非常之多,就比如上面的divisionnumber函数,他所在的地址信息多数时候我们并不关心,为了屏蔽这些“垃圾”信息,我们可以在代码中加一行:

better_exceptions.MAX_LENGTH = XXX

XXX是允许显示的最大字符长度,比如这里设置为10,再来运行better_test.py这个程序就会是下面的结果:

可以看到,对函数divisionnumber的注释只显示了最开始的"<function"这几个字符。
除了上面提到的功能之外,better-exceptions还可以和logging还有django无缝接入,这使得它的应用更加灵活,关于这方面内容大家可以查看项目文档。
还有一点需要提醒大家,如果你是在windows下使用,可能会出现下图中的乱码问题,这是由于better-exceptions的内设编码格式所导致的。

解决的办法是在安装后,对better_exceptions目录下的encoding.py文件第10行代码进行如下修改:

# 原代码:
ENCODING = locale.getpreferredencoding()
# 修改为:
ENCODING = ‘utf-8‘

3.PySnooper

PySnooper也是一个Python的第三方库,他的特点是能够精准的显示每条代码的执行顺序、执行时间以及随之带来的局部变量的改变等等。值得一提的是,作为一个发布不满半年的库,PySnooper在github上已经达到了1.2W星,其受欢迎程度可见一斑。

PySnooper的使用可以说是非常的方便,直接在代码中以装饰器的形式调用就可以了。当然在引用前你得使用pip install pysnooper或者conda install -c conda-forge pysnooper安装这个库。我们还是举一个例子来进行演示,样例代码如下:

‘‘‘
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
‘‘‘
import pysnooper
import random
@pysnooper.snoop()
def foo():
    lst = []
    for i in range(10):
        lst.append(random.randrange(1, 1000))
    lower = min(lst)
    upper = max(lst)
    mid = (lower + upper) / 2
    print(lower, mid, upper)
foo()

在上面这段代码中,我们先是生成10个1到1000之间的随机数,然后计算他们之中的最大最小值和中位数,唯一的不同在于第三行多了一条语句@pysnooper.snoop(),我们运行以下代码,发现除了正常的print结果之外,多了许多内容(内容太多,下面只显示一部分):

19:51:57.704857 call        16 def foo():
19:51:57.705860 line 17     lst = []
New var:....... lst = []
19:51:57.705860 line 18     for i in range(10):
New var:....... i = 0
19:51:57.705860 line 19         lst.append(random.randrange(1, 1000))
Modified var:.. lst = [758]
19:51:57.705860 line 18     for i in range(10):
Modified var:.. i = 1

....................

19:51:57.706818 line 22     upper = max(lst)
New var:....... upper = 927
19:51:57.706818 line 23     mid = (lower + upper) / 2
New var:....... mid = 552.0
19:51:57.706818 line 24     print(lower, mid, upper)
19:51:57.706818 return      24     print(lower, mid, upper)
Return value:.. None

这都是PySnooper跟踪监控的结果,正如上面所说,他准确记录的每条代码的运行时间、顺序以及相关的变量值。
作为一个星标1.2W+的项目,PySnooper的功能肯定不会这么简单,@pysnooper.snoop()中是可以接收参数的,比如我们觉得输出内容太多,可以考虑把信息记录到log日志中,这个功能只需要加一个log文件定位参数就能搞定:

@pysnooper.snoop(‘file.log‘)

@pysnooper.snoop()支持的参数还有很多,分别对应了不同的功能,例如监控自定义表达式、监控底层函数、支持多线程等等,详见项目文档。
此外,pysnooper还支持局部监控,一般来说我们写的代码都比较长,而需要监控的只是其中的一小部分,这时候就可以把需要监控的代码放到一个block里。我们修改下刚才的代码,只对计算最大最小值和中位数的部分进行监控,修改后的代码如下:

‘‘‘
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
‘‘‘
import pysnooper
import random
def foo():
    lst = []
    for i in range(10):
        lst.append(random.randrange(1, 1000))
    with pysnooper.snoop():
      lower = min(lst)
      upper = max(lst)
      mid = (lower + upper) / 2
    print(lower, mid, upper)
foo()

运行之后发现监控信息精简了很多:

New var:....... lst = [562, 341, 552, 353, 628, 302, 430, 188, 955, 108]
New var:....... i = 9
20:02:47.359272 line 21       lower = min(lst)
New var:....... lower = 108
20:02:47.359272 line 22       upper = max(lst)
New var:....... upper = 955
20:02:47.360269 line 23       mid = (lower + upper) / 2

使用with pysnooper.snoop()模式依然保留了对各种参数的支持,个人认为这种模式更加符合实践需求。

小结:
今天介绍了三个不借助IDE就能方便使用的调试工具,三个工具的调试思路和适用场景也各不相同,大家可以根据需要灵活选用。不过话说回来,我个人最喜欢的还是PySnooper,你最喜欢哪一款呢?

原文地址:https://blog.51cto.com/14246112/2442417

时间: 2024-10-30 05:52:34

Python里三个好用的调试神器的相关文章

python里三种等待元素的方法

在做web或app的自动化测试经过会出现找不到元素而报错的情况,很多时候是因为元素 还没有被加载出来,查找的代码就已经被执行了,自然就找不到元素了.那么我可以用等待 元素加载完成后再执行查找元素的code. Python里有三种等待的方式:一. 强制等待 Sleep(54) 这个方法在time模块,使用时通过from time import sleep导入比如: Sleep(10) #表示强行等待10s再执行下一句代码 Driver.find_element_by_xpath("xxxxxx&q

Python进阶(三十五)-Fiddler命令行和HTTP断点调试

Python进阶(三十五)-Fiddler命令行和HTTP断点调试 一. Fiddler内置命令 ??上一节(使用Fiddler进行抓包分析)中,介绍到,在web session(与我们通常所说的session不是同一个概念,这里的每条HTTP请求都称为一个session).界面中能够看到Fiddler抓取的全部HTTP请求.而为了更加方便的管理全部的session, Fiddler提供了一系列内置的函数用于筛选和操作这些session(习惯命令行操作Linux的童鞋应该能够感受到这会有多么方便

python第三章

一.三目运算 也叫三元运算,例如result=x if x<y else y 二.集合(set) 集合(set):把不同的元素组成一起形成集合,是python基本的数据类型. 集合元素(set elements):组成集合的成员 1 >>> li=['a','b','c','a'] 2 >>> se =set(li) 3 >>> se 4 set(['a', 'c', 'b']) 集合对象是一组无序排列的可哈希的值:集合成员可以做字典的键 >

加壳学习笔记(三)-简单的脱壳思路&amp;调试思路

首先一些windows的常用API: GetWindowTextA:以ASCII的形式的输入框 GetWindowTextW:以Unicaode宽字符的输入框 GetDlgItemTextA:以ASCII的形式的输入框 GetDlgItemTextW:以Unicaode宽字符的输入框 这些函数在使用的时候会有些参数提前入栈,如这函数要求的参数是字符串数目.还有大小写啦之类的东西,这些东西是要在调用该函数之前入栈,也就是依次push,就是说一般前面几个push接着一个call,那前面的push可能

Python进阶(三十四)-Python3多线程解读

Python进阶(三十四)-Python3多线程解读 线程讲解 ??多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度. 程序的运行速度可能加快. 在一些等待的任务实现上如用户输入.文件读写和网络收发数据等,线程就比较有用了.在这种情况下我们可以释放一些珍贵的资源如内存占用等等. ??线程在执行过程中与进程还是有区别的.每个独立

Unity3D热更新全书-脚本(三) C#LightEvil语法与调试

调试,这是一个无法规避的问题 C#Light 由于有 词法解释.语法解释.运行时三种情况 所以和C#也是有类似的问题 出错大致可以分为编译错误和运行时错误 拼写出莫名的东西或者语法不正确,会在编译阶段报错,这种错误很好检查,因为 C#Light语法是C#的严格子集,所有的C#Light脚本都可以用C#的标准做语法检查 这也是C#Light基本上是用VisualStudio做编辑器的原因所在,直接作为C#代码编译,可以排除大部分的语法问题. 然后剩下的一些作为C#代码可以编译过,但是C#Light

Python进阶(三十九)-数据可视化の使用matplotlib进行绘图分析数据

Python进阶(三十九)-数据可视化の使用matplotlib进行绘图分析数据 ??matplotlib 是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图.而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中. ??它的文档相当完备,并且 Gallery页面 中有上百幅缩略图,打开之后都有源程序.因此如果你需要绘制某种类型的图,只需要在这个页面中浏览/复制/粘贴一下,基本上都能搞定. ??在Linux下比较著名的数据图工具还有gnuplot

python里的拆包、引用、递归与匿名函数

拆包:*A拆元组,**B拆字典. 引用:在C.C++里面里面a=1,b=a,实际上相当于硬链接,相当于两份a.b各有一个1,多占一个空间,而在python里就是软连接,只有一份,通过id(a)来查看id都一样, 在python里定义的东西如一直没引用,那么就会成为垃圾,不用担心,python有自己的机制去除垃圾. 不可变类型:数字.字符串.元组. 可变类型:列表.字典,它们都不能做字典的key. 递归:一个函数里调用了这个函数自己,递归完成阶乘,3的阶乘=3*(2的阶乘) 递归时一定要想到何时要

Python里那些深不见底的“坑”

Python是一门清晰简洁的语言,如果你对一些细节不了解的话,就会掉入到那些深不见底的"坑"里,下面,我就来总结一些Python里常见的坑. 列表创建和引用 嵌套列表的创建 使用*号来创建一个嵌套的list: li = [[]] * 3 print(li) # Out: [[], [], []] 通过这个方法,可以得到一个包含3个list的嵌套list,我们来给第一个list增加一个元素: li[0].append(1) print(li) # Out: [[1], [1], [1]]