对于每一个程序开发者来说,调试几乎是必备的技能。代码卡在写的中间,不知道这个函数执行后的返回结果是什么?调试一下,看到代码运行到一半就报错了。什么情况?为什么和预期的不一样?调试看看调试方式多种多样,不同的调试方式适用于不同的场景和人群。如果你是编程新手,对很多工具的使用不是很熟练,那么print和log就不错了。如果在本地(Win或Mac)电脑上开发,那么IDE的图形界面调试无疑是最好的。合适的;如果你是排查服务器上的bug,那么使用PDB进行无图形界面的调试应该是首选。详情请点击明哥上一篇文章:让代码调试不再难-pdb如果你想在本地开发,但是项目的进度需要依赖复杂的服务器环境,那么可以学习一下PyCharm的远程调试。详情请戳明前文:除了上面这些,明今天还要给大家介绍一个非常好用的。是一款在某些场景下可以大大提高调试效率的调试工具。那就是PySnooper,在Github上获得了13kstar,获得了大家的一致好评。有了这个工具,连小萌新都可以无门槛直接上手,从此告别打印~1.快速安装执行以下命令安装PySnooper$python3-mpipinstallpysnooper#or$condainstall-cconda-forgepysnooper#or$yay-Spython-pysnooper2。简单案例下面的代码定义了一个demo_func函数,在里面生成一个profile字典变量,然后更新它,最后返回。代码本身没有实际意义,但足以演示PySnooper。importpysnooper@pysnooper.snoop()defdemo_func():profile={}profile["name"]="写代码的小明"profile["age"]=27profile["gender"]="male"returnprofiledefmain():profile=demo_func()main()现在我使用终端命令行来运行它[root@iswbm~]#python3demo.pySourcepath:...demo.py17:52:49.624943call4defdemo_func():17:52:49.625124line5profile={}newvar:.......profile={}17:52:49.625156line6profile["name"]="写代码的明哥"Modifiedvar:..profile={'name':'明哥写的代码'}17:52:49.625207line7profile["age"]=27Modifiedvar:..profile={'name':'写代码的明哥','age':27}17:52:49.625254line8profile["gender"]="male"Modifiedvar:..profile={'name':'写代码的小明','age':27,'gender':'male'}17:52:49.625306line10returnprofile17:52:49.625344return10returnprofileReturnvalue:..{'name':'写代码的明哥','age':27,'gender':'male'}Elapsedtime:00:00:00.000486可以看到PySnooper记录了函数运行的所有过程,包括:代码片段、行号等信息,每行代码什么时候调用?函数内局部变量的值如何变化?何时添加变量以及何时修改变量。函数的返回值是多少?该函数运行了多少时间?作为一个开发者,想要得到如此详细的调试信息,你需要做的很简单,给你要调试的函数戴上帽子(装饰器)——@pysnooper.snoop()即可。3、详细使用2.1重定向到日志文件@pysnooper.snoop()不带任何参数,默认将调试信息输出到标准输出。对于单次调试就可以解决的bug,没有问题,但有些bug只是在特定的场景下才会出现,需要把程序放到后面运行一段时间才能重现。在这种情况下,您可以将调试信息输出重定向到日志文件,以便于跟踪。@pysnooper.snoop(output='/var/log/debug.log')defdemo_func():...2.2跟踪非局部变量值PySnooper是以函数为单位进行调试的,它只跟踪局部变量的值函数体默认是变量,如果要跟踪全局变量,可以在pysnooper.snoop()中添加watch参数out={"foo":"bar"}@pysnooper.snoop(watch=('out["foo"]'))defdemo_func():...这样,当out["foo"]的值发生变化时,PySnooper也会打印出来。watch参数接收一个可迭代对象(可以是列表或元组),里面的元素是字符串表达式,什么意思?看下面的例子就知道@pysnooper.snoop(watch=('out["foo"]','foo.bar','self.foo["bar"]'))defdemo_func():...和watch相比之下,pysnooper.snoop()还可以接收一个函数watch_explode,表示监听除了这些参数之外的所有其他全局变量。@pysnooper.snoop(watch_explode=('foo','bar'))defdemo_func():...2.3设置trace函数的深度当你使用PySnooper调试一个函数时,如果函数中调用了其他函数,PySnooper不会傻傻地跟着它。如果想继续跟踪该函数调用的其他函数,可以通过指定depth参数来设置跟踪深度(不指定则默认为1)。@pysnooper.snoop(depth=2)defdemo_func():...2.4设置debuglog的前缀当使用PySnooper跟踪多个函数时,debuglog会显得杂乱无章,不方便查看。在这种情况下,PySnooper提供了一个参数,可以让你为不同的功能设置不同的标志,以便你在查看日志时能够区分。@pysnooper.snoop(output="/var/log/debug.log",prefix="demo_func:")defdemo_func():...效果如下2.5设置最大输出长度默认情况下,变量和异常PySnooper输出的信息,如果超过100个字符,将被截断为100个字符。当然,您也可以通过指定参数@pysnooper.snoop(max_variable_length=200)defdemo_func():...来修改它。您也可以使用max_variable_length=None,它永远不会截断它们。@pysnooper.snoop(max_variable_length=None)defdemo_func():...2.6支持多线程调试模式PySnooper也支持多线程调试,通过设置参数thread_info=True,它会在日志中打印出哪些线程对修改做成变量。@pysnooper.snoop(thread_info=True)defdemo_func():...效果如下2.7自定义对象的格式输出pysnooper.snoop()函数有一个参数是custom_repr,接收一个元组对象。在这个元组中,您可以指定特定类型的对象以特定格式输出。这里我举个例子。如果我要跟踪Person类型的对象,PySnooper不能正常输出它的信息,因为它不是常规的Python基本类型。所以我在pysnooper.snoop()函数中设置了custom_repr参数,第一个元素是Person,第二个元素是print_persion_obj函数。PySnooper在打印某个对象的调试信息时,会一一判断是否为Person类型的对象。如果是,它会将对象传递给print_persion_obj函数,函数将决定如何显示这个对象的信息。classPerson:通过defprint_person_obj(obj):returnf"
