千家信息网

Python编程为什么不使用print调试代码了

发表于:2024-12-12 作者:千家信息网编辑
千家信息网最后更新 2024年12月12日,这篇文章主要介绍"Python编程为什么不使用print调试代码了",在日常操作中,相信很多人在Python编程为什么不使用print调试代码了问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作
千家信息网最后更新 2024年12月12日Python编程为什么不使用print调试代码了

这篇文章主要介绍"Python编程为什么不使用print调试代码了",在日常操作中,相信很多人在Python编程为什么不使用print调试代码了问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Python编程为什么不使用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"] = 27    profile["gender"] = "male"    return profiledef main():    profile = demo_func()main()

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

[root@iswbm ~]# python3 demo.py Source path:... demo.py17: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"] = 27Modified 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 profile17:52:49.625344 return      10     return profileReturn value:.. {'name': '写代码的明哥', 'age': 27, 'gender': 'male'}Elapsed time: 00:00:00.000486

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

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

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

函数的返回值是什么?

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

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

3. 详细使用

3.1 重定向到日志文件

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

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

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

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

3.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():                ...

3.3 设置跟踪函数的深度

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

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

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

3.4 设置调试日志的前缀

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

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

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

效果如下

3.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():    ...

3.6 支持多线程调试模式

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

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

效果如下

3.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""  @pysnooper.snoop(custom_repr=(Person, print_person_obj))def demo_func():    ...

完整的代码如下

import pysnooperclass Person:passdef print_person_obj(obj):    return f""@pysnooper.snoop(custom_repr=(Person, print_person_obj))def demo_func():    person = Person()    person.name = "写代码的明哥"    person.age = 27    person.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():    ...

到此,关于"Python编程为什么不使用print调试代码了"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

函数 代码 参数 对象 变量 跟踪 输出 信息 类型 明哥 日志 编程 元素 写法 学习 运行 字符 局部 情况 效果 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全宣传教育课 计算机网络技术学成是做什么 软件开发公司哪里弄单子 学生网络安全班会策划书 数据库中数据为空怎么表示 无线自组织网络安全性 在数据库中两级映像怎么建立的 聊城苹果软件开发哪家便宜 统计学计算器怎么建立数据库 数据库 case 的用法 MFC的MRI软件开发 2050年网络安全宣传主题 网络安全与执法类职位 珠海嵌入式软件开发流程费用 软件开发中会议纪要类型 软件开发的基本要素 顺义区质量软件开发检修 明宇软件开发有限公司怎么样 网络技术总监简历 服务器一直没关机的游戏 公民维护网络安全的权利 网络安全图书读后感 哪一个不属于数据库管理系统 达梦数据库有限公司股票代码 服务器吞吐量计算 计算机网络技术类与网络工程专业 服务器网卡芯片型号如何查找 保税科技和互联网 网络技术和通信技术结合 火球雕文魔兽数据库
0