当前位置: 首页 > 科技观察

Python中的三款优质调试神器

时间:2023-03-13 20:47:40 科技观察

调试是开发过程中不可避免的一环。在Python中,我们使用print、logging、assert等方法进行调试,简单实用,但毕竟有其局限性。今天的文章为大家带来了三个工具,包括Python内置模块和第三方库,它们提供了调试代码所需的大部分常用功能,将大大提高我们的开发和bug修复效率。1.PDBpdb是Python中的内置模块。开启pdb后,可以设置断点,跟踪调试代码。为了演示方便,我们准备了一个示例程序pdb_test.py:defcountnumber(number):foriinrange(number):print(i)if__name__=='__main__':countnumber(10)然后在输入python-mpdbpdb_test终端。py命令,进入pdb的调试模式:此时我们可以通过各种命令控制代码执行或者查看当前变量,比如l可以查看所有代码,n是执行下一段代码,p可以查看当前变量等,需要注意的是,命令n只会执行主程序中的代码。如果要单步执行子函数中的代码,需要使用s命令。调试效果如下:可以看到通过s命令(如果你只想在main函数中单步执行n)和p命令可以单步执行。我们控制程序单步运行,实时查看相关变量。但是单步执行毕竟是一种非常低效的调试方式,尤其是在代码量比较大的时候,更是噩梦。这时候就需要用到pdb的set_trace()方法了。下面对示例程序pdb_test.py做一点修改:importpdbdefcountnumber(number):foriinrange(number):print(i)pdb.set_trace()if__name__=='__main__':countnumber(10)pdb.set_trace的函数()是在代码中设置断点,在pdb调试模式下,使用c命令直接跳转到下一个断点位置。如果之后没有其他断点,则将执行整个代码。调试效果如下:除了上述几条指令外,pdb还有其他比较常用的命令(见下表),综合使用基本可以满足日常调试需要。2.Better-exceptionsbetter-exceptions是一个Python第三方库,作者对他的定义是“让异常信息更漂亮更详细”。在正式使用之前先说一下这个库的安装:第一步,使用pipinstallbetter_exceptions安装better-exceptions库;第二步,使用exportBETTER_EXCEPTIONS=1(Linux/OSX)或setxBETTER_EXCEPTIONS1(Windows)设置环境变量。现在可以使用better-exceptions正常调试了。为了让演示效果更明显,我们将上面的代码稍微修改为本示例程序better_test.py:defdivisionnumber(number,div):foriinrange(div):print(number/i)if__name__=='__main__':divisionnumber(10,10)显然,上面的代码会因为执行时分母为0而抛出异常。现在我们执行pythonbetter_test.py,看看开启better-exceptions后异常信息是什么样的:从上图我们可以看出,better-exceptions对异常信息的修改主要体现在两个方面:一是对产生异常的代码进行颜色编码;2在产生异常的代码中输出相关的变量值(包括函数和其他对象);这样,在很多情况下,我们只需要根据better-exceptions输出的辅助信息来判断异常的位置和原因,而不必像之前那样再次检查源码,观察运行结果,正如作者所说说:漂亮,比较有帮助。但是过多的信息输出也会带来问题,就是当代码层次结构比较复杂的时候,better-exceptions输出的辅助信息可能会非常多,比如上面的divisionnumber函数,它的地址信息大部分都是time我们不管,为了屏蔽这些“垃圾”信息,我们可以在代码中添加一行:better_exceptions.MAX_LENGTH=XXXXXX是允许显示的最大字符长度,比如这里设置为10,然后运行程序better_test.py会得到如下结果:可以看到函数divisionnumber的注释只显示了初始的"除了上面提到的函数,better-exceptions还可以和logging和logging无缝对接django,这使得它的应用更加灵活,这方面可以查看项目文档。还有一点要提醒大家,如果在windows下使用,可能会出现乱码下图中的ter,是better-exceptions内置的编码格式导致的解决办法是安装后better_exceptions目录下encoding.py文件第10行代码修改如下:#原码:ENCODING=locale.getpreferredencoding()#改为:ENCODING='utf-8'3.PySnooperPySnooper也是Python的第三方库。它的特点是可以准确显示每段代码的执行顺序,执行时间以及后续局部变量的变化等。值得一提的是,作为一个发布了半年的未满库,PySnooper在github上的star数已经达到1.2W,其受欢迎程度可见一斑。PySnooper的使用可以说是非常方便,直接在代码中作为装饰器调用即可。当然,在引用之前你必须使用pipinstallpysnooper或condainstall-cconda-forgepysnooper安装这个库。下面举个例子来演示一下,示例代码如下:importpysnooperimportrandom@pysnooper.snoop()deffoo():lst=[]foriinrange(10):lst.append(random.randrange(1,1000))lower=min(lst)upper=max(lst)mid=(lower+upper)/2print(lower,mid,upper)foo()在上面的代码中,我们先生成10个1到1000之间的随机数,然后计算最大值,其中最小值和中值。唯一不同的是第三行多了一条语句@pysnooper.snoop()。我们运行下面的代码,发现除了正常的打印结果,还有很多内容(内容太多,下面只展示了一部分):19:51:57.704857call16deffoo():19:51:57.705860line17lst=[]Newvar:.......lst=[]19:51:57.705860line18foriinrange(10):Newvar:......i=019:51:57.705860line19lst.append(random.randrange(1,1000))Modifiedvar:..lst=[758]19:51:57.705860line18foriinrange(10):Modifiedvar:..i=1...............19:51:57.706818line22upper=max(lst)Newvar:......upper=92719:51:57.706818line23mid=(lower+upper)/2Newvar:......mid=552.019:51:57.706818line24print(lower,mid,upper)19:51:57.706818return24print(lower,mid,upper)返回值:..None这是PySnooper跟踪监控的结果。上面说到,他准确地记录了每段代码的运行时间、顺序和相关的变量值。作为一个星标1.2W+的项目,PySnooper的功能肯定不会这么简单。@pysnooper.snoop()可以接收参数。比如我们觉得输出的内容太多,可以考虑将信息记录在log日志中。该函数只需要添加一个日志文件定位参数即可获取:@pysnooper.snoop('file.log')@pysnooper.snoop()支持很多参数,分别对应不同的函数,比如监控自定义表达式,监控底层功能,支持多线程等,详见项目文档。此外,pysnooper还支持局部监控。一般来说,我们写的代码比较长,需要监控的只有一小部分。这时候可以把需要监控的代码放到一个block里面。我们修改一下刚才的代码,只监控计算最大值、最小值和中值的部分。修改后的代码如下:importpysnooperimportrandomdeffoo():lst=[]foriinrange(10):lst.append(random.randrange(1,1000))withpysnooper.snoop():lower=min(lst)upper=max(lst)mid=(lower+upper)/2print(lower,mid,upper)foo()运行后发现监控信息简化了很多:Newvar:.......lst=[562,341,552,353,628,302,430,188,955,108]Newvar:.......i=920:02:47.359272line21lower=min(lst)Newvar:......lower=10820:02:47.359272line22upper=max(lst)Newvar:......upper=95520:02:47.360269line23mid=(lower+upper)/2usingwithpysnooper.snoop()模式还是保留了我个人认为这种模式更符合实际需要。总结:今天给大家介绍三款不借助IDE也能轻松上手的调试工具。三种工具的调试思路和适用场景也各不相同,大家可以根据自己的需要灵活选择。不过话说回来,我个人最喜欢的还是PySnooper,你最喜欢哪一个呢?