阅读本文大概需要 5 分钟。

大家好

对于每个程序开发者来说,调试几乎是必备技能。

代码写到一半卡住了,不知道这个函数执行完的返回结果是怎样的?调试一下看看

代码运行到一半报错了,什么情况?怎么跟预期的不一样?调试一下看看

调试的方法多种多样,不同的调试方法适合不同的场景和人群。

  • 如果你是刚接触编程的小萌新,对很多工具的使用还不是很熟练,那么 print 和 log 大法好

  • 如果你在本地(Win或者Mac)电脑上开发,那么 IDE 的图形化界面调试无疑是最适合的;

  • 如果你在服务器上排查BUG,那么使用 PDB 进行无图形界面的调试应该是首选。

  • 如果你要在本地进行开发,但是项目的进行需要依赖复杂的服务器环境,那么可以了解下 PyCharm 的远程调试。

除了以上,今天再给你介绍一款非常好用的调试工具,它能在一些场景下,大幅度提高调试的效率, 那就是 PySnooper,它在 Github 上已经收到了 13k 的 star,获得大家的一致好评。

有了这个工具后,就算是小萌新也可以直接无门槛上手,从此与 print 说再见~

1. 快速安装

执行下面这些命令进行安装 PySnooper

$ python3 -m pip install pysnooper# 或者
$ conda install -c conda-forge pysnooper# 或者
$ yay -S python-pysnooper

2. 简单案例

下面这段代码,定义了一个 demo_func 的函数,在里面生成一个 profile 的字典变量,然后去更新它,最后返回。

代码本身没有什么实际意义,但是用来演示 PySnooper 已经足够。

import pysnooper@pysnooper.snoop()
def demo_func():profile = {}profile["name"] = "写代码的明哥"profile["age"] = 27profile["gender"] = "male"return profiledef main():profile = demo_func()main()

现在我使用终端命令行的方式来运行它

[root@iswbm ~]# python3 demo.py
Source path:... demo.py
17:52:49.624943 call         4 def demo_func():
17:52:49.625124 line         5     profile = {}
New var:....... profile = {}
17:52:49.625156 line         6     profile["name"] = "写代码的明哥"
Modified var:.. profile = {'name': '写代码的明哥'}
17:52:49.625207 line         7     profile["age"] = 27
Modified var:.. profile = {'name': '写代码的明哥', 'age': 27}
17:52:49.625254 line         8     profile["gender"] = "male"
Modified var:.. profile = {'name': '写代码的明哥', 'age': 27, 'gender': 'male'}
17:52:49.625306 line        10     return profile
17:52:49.625344 return      10     return profile
Return value:.. {'name': '写代码的明哥', 'age': 27, 'gender': 'male'}
Elapsed time: 00:00:00.000486

可以看到 PySnooper 把函数运行的过程全部记录了下来,包括:

  • 代码的片段、行号等信息,以及每一行代码是何时调用的?

  • 函数内局部变量的值如何变化的?何时新增了变量,何时修改了变量。

  • 函数的返回值是什么?

  • 运行函数消耗了多少时间?

而作为开发者,要得到这些如此详细的调试信息,你需要做的非常简单,只要给你想要调试的函数上带上一顶帽子(装饰器) -- @pysnooper.snoop() 即可。

3. 详细使用

2.1 重定向到日志文件

@pysnooper.snoop() 不加任何参数时,会默认将调试的信息输出到标准输出。

对于单次调试就能解决的 BUG ,这样没有什么问题,但是有一些 BUG 只有在特定的场景下才会出现,需要你把程序放在后面跑个一段时间才能复现。

这种情况下,你可以将调试信息重定向输出到某一日志文件中,方便追溯排查。

@pysnooper.snoop(output='/var/log/debug.log')
def demo_func():...

2.2 跟踪非局部变量值

PySnooper 是以函数为单位进行调试的,它默认只会跟踪函数体内的局部变量,若想跟踪全局变量,可以给 pysnooper.snoop() 加上 watch 参数

out = {"foo": "bar"}@pysnooper.snoop(watch=('out["foo"]'))
def demo_func():...

如此一来,PySnooper 会在 out["foo"] 值有变化时,也将其打印出来

watch 参数,接收一个可迭代对象(可以是list 或者 tuple),里面的元素为字符串表达式,什么意思呢?看下面例子就知道了

@pysnooper.snoop(watch=('out["foo"]', 'foo.bar', 'self.foo["bar"]'))
def demo_func():...

watch 相对的,pysnooper.snoop() 还可以接收一个函数 watch_explode,表示除了这几个参数外的其他所有全局变量都监控。

@pysnooper.snoop(watch_explode=('foo', 'bar'))
def demo_func():...

2.3 设置跟踪函数的深度

当你使用 PySnooper 调试某个函数时,若该函数中还调用了其他函数,PySnooper 是不会傻傻的跟踪进去的。

如果你想继续跟踪该函数中调用的其他函数,可以通过指定 depth 参数来设置跟踪深度(不指定的话默认为 1)。

@pysnooper.snoop(depth=2)
def demo_func():...

2.4 设置调试日志的前缀

当你在使用 PySnooper 跟踪多个函数时,调试的日志会显得杂乱无章,不方便查看。

在这种情况下,PySnooper 提供了一个参数,方便你为不同的函数设置不同的标志,方便你在查看日志时进行区分。

@pysnooper.snoop(output="/var/log/debug.log", prefix="demo_func: ")
def demo_func():...

效果如下

2.5 设置最大的输出长度

默认情况下,PySnooper 输出的变量和异常信息,如果超过 100 个字符,被会截断为 100 个字符。

当然你也可以通过指定参数 进行修改

@pysnooper.snoop(max_variable_length=200)
def demo_func():...

您也可以使用max_variable_length=None它从不截断它们。

@pysnooper.snoop(max_variable_length=None)
def demo_func():...

2.6 支持多线程调试模式

PySnooper 同样支持多线程的调试,通过设置参数 thread_info=True,它就会在日志中打印出是在哪个线程对变量进行的修改。

@pysnooper.snoop(thread_info=True)
def demo_func():...

效果如下

2.7 自定义对象的格式输出

pysnooper.snoop() 函数有一个参数是 custom_repr,它接收一个元组对象。

在这个元组里,你可以指定特定类型的对象以特定格式进行输出。

这边我举个例子。

假如我要跟踪 person 这个 Person 类型的对象,由于它不是常规的 Python 基础类型,PySnooper 是无法正常输出它的信息的。

因此我在 pysnooper.snoop() 函数中设置了 custom_repr 参数,该参数的第一个元素为 Person,第二个元素为 print_persion_obj 函数。

PySnooper 在打印对象的调试信息时,会逐个判断它是否是 Person 类型的对象,若是,就将该对象传入 print_persion_obj 函数中,由该函数来决定如何显示这个对象的信息。

class Person:passdef print_person_obj(obj):return f"<Person {obj.name} {obj.age} {obj.gender}>"@pysnooper.snoop(custom_repr=(Person, print_person_obj))
def demo_func():...

完整的代码如下

import pysnooperclass Person:passdef print_person_obj(obj):return f"<Person {obj.name} {obj.age} {obj.gender}>"@pysnooper.snoop(custom_repr=(Person, print_person_obj))
def demo_func():person = Person()person.name = "写代码的明哥"person.age = 27person.gender = "male"return persondef main():profile = demo_func()main()

运行一下,观察一下效果。

如果你要自定义格式输出的有很多个类型,那么 custom_repr 参数的值可以这么写

@pysnooper.snoop(custom_repr=((Person, print_person_obj), (numpy.ndarray, print_ndarray)))
def demo_func():...

还有一点我提醒一下,元组的第一个元素可以是类型(如类名Person 或者其他基础类型 list等),也可以是一个判断对象类型的函数。

也就是说,下面三种写法是等价的。

# 【第一种写法】
@pysnooper.snoop(custom_repr=(Person, print_persion_obj))
def demo_func():...# 【第二种写法】
def is_persion_obj(obj):return isinstance(obj, Person)@pysnooper.snoop(custom_repr=(is_persion_obj, print_persion_obj))
def demo_func():...# 【第三种写法】
@pysnooper.snoop(custom_repr=(lambda obj: isinstance(obj, Person), print_persion_obj))
def demo_func():...

以上就是今天给大家介绍的一款调试神器(PySnooper) 的详细使用手册,是不是觉得还不错?

如果你还有其他关于调试的技巧,可以留言区分享出来,一起学习一下~

由于微信平台算法改版,公号内容将不再以时间排序展示,如果大家想第一时间看到我们的推送,强烈建议星标我们和给我们多点点【在看】。星标具体步骤为:

(1)点击页面最上方“小詹学Python”,进入公众号主页。
(2)点击右上角的小点点,在弹出页面点击“设为星标”,就可以啦。
感谢支持,比心。

求你了,别再用 print 调试代码了相关推荐

  1. 学了编程却写出错误代码?程序运行结果与想象不符?当bug出现时该何去何从,别担心,这篇文章统统告诉你!手把手带你调试代码,让bug原形毕露!

    [手把手带你搞定]实用调试技巧 什么是bug 调试及调试的重要性 什么是调试 调试的基本步骤 1. 发现程序错误的存在 2. 以隔离.消除等方式对错误进行定位 3. 确定错误产生的原因 4. 提出纠正 ...

  2. 答应我!忘了他吧!不要再用print了,以后调试Python用冰淇淋

    大家好,我是Lex 喜欢欺负超人那个Lex 擅长领域:python开发.网络安全渗透.Windows域控Exchange架构 今日重点: 调试python不要再用print()了,甜甜的冰激凌调试工具 ...

  3. 答应我,调试Python代码,不要再用Print了!

    相信大部分人学习Python,肯定会用print()这个内置函数,来调试代码的. 那么在一个大型的项目中,如果你也是使用print来调试你的Python代码,你就会发现你的终端有多个输出. 那么你便不 ...

  4. python学习-代码调试(通过print调试、通过pdb调试、通过编译器调试(断点调试))

    文章目录 通过print调试 通过pdb调试 通过编译器调试(断点调试) 通过print调试 首先我这里写了一段代码,保存列表中的偶数,然后返回列表,在函数执行后,我对函数结果进行打印. 见代码: l ...

  5. java中有测试方法主方法不运行_java – 我的Eclipse无法再运行(或调试)我的JUnit测试...

    我使用 Eclipse IDE:面向Web开发人员的Eclipse Java EE IDE.版本:Indigo Release 我使用JUnit 4.对于项目属性> Java构建路径>库: ...

  6. unity中请问如何点击一下image(image加了button项)变红色再点击一下按钮变成绿色。一直这么循环变色?急求,望大佬指点emmm。c#代码

    unity中请问如何点击一下image(image加了button项)变红色再点击一下按钮变成绿色.一直这么循环变色?急求,望大佬指点emmm.c#代码

  7. java后门_@Java Web 程序员,我们一起给程序开个后门吧:让你在保留现场,服务不重启的情况下,执行我们的调试代码...

    从数据库.redis取了些数据,做了一些运算后,没抛异常,但是就是结果不对 抛了个空指针异常,但是看代码,感觉没问题,是取出来就是空,还是中间什么函数把它改坏了 发现导致一个bug的原因是用了JVM缓 ...

  8. html禁止f12键代码,网站禁用f12 禁止调试代码方法

    正常情况下 自己的一些网站的一些代码不想被别人扒来扒去的,可以看看本方法. 可以设置复制提示,禁f12,禁止调试是很有必要的 当然这个防不了大佬,只能防防小学生 具体方法: 1.首先我们需要下载web ...

  9. mooc-IDEA 调试代码--012

    mooc-IDEA 调试代码 添加断点快捷键:ctrl+F8 单步运行:F9  <=>resum(从一个断点跳转到下一个断点) 一行一行运行:F8 查看所有断点: 禁止所有断点: 条件断点 ...

最新文章

  1. 乐观锁 CAS 悲观锁synchronized()性能低因为涉及操作系统内核与用户模式的切换
  2. java 注入 循环_spring依赖注入——循环依赖
  3. 汇编语言:实现大小写字母转换
  4. sklearn 常用模块及类与方法
  5. 吴恩达二月升级当爸,Jeff Dean打趣:无监督学习要开始了,需要数据集么?
  6. ModalPopupExtender使用技巧( operate ModalPopupExtender by JavaScript)
  7. 如何解读vmlinux.lds.S文件
  8. 修改Gravatar生成的默认头像
  9. visio画图复制粘贴到word_怎么将visio绘图导入Word(visio绘图插入word后如何调整大小)...
  10. 新萝卜家园 GhostXP_SP3 五一纪念版
  11. 健身为什么要吃肌酸!肌酸四大作用
  12. word排版快捷指令_在word文档中如何利用快捷键快速排版呢?
  13. CentOS7通过SpeedTest工具网络测速
  14. MySQL查询数据库表记录数
  15. 基于opencv的SVM车牌号码识别模型训练(C++)QT
  16. Linux运维:推荐八款Linux远程连接工具
  17. linux学习之linux百问,不断更新
  18. java实训小项目6_实训项目
  19. c刊计算机领域见刊快的期刊,见刊快的核心期刊_见刊快的核心期刊_好投的医学核心期刊...
  20. java实现人物关系预测_用 NetworkX + Gephi + Nebula Graph 分析人物关系(上篇)

热门文章

  1. 【IDEA】2020 断点(BreakPoints)调试(亲测)
  2. 小白学习机器学习---第六章:SVM算法原理(1)
  3. 双十一大促技术只做两件事情?来看看阿里巴巴的技术之道
  4. git commit提示Your branch is up-to-date with 'origin/master'.(做过测试不错)
  5. MySQL分页查询慢的方案解决
  6. linux双核对线程,Linux检查双核(及多核)CPU信息
  7. matlab 填充数组,Matlab自动将数组类型从复数转换为double
  8. highcharts一天时间 与一周时间_一天当中什么时间减肥降重最好的
  9. 【SSL】【Apache】 使用向导配置 https/ssl
  10. 向js中添加静态方法与属性方法