【Python调试】Python中调试模块

本文介绍了Python的两个调试工具,icecream和pysnooper。icecream提供了一种优雅的方式来输出变量和函数值,支持自定义前缀和输出位置。pysnooper则是非侵入式的调试方式,详细记录函数执行过程,包括变量变化、函数调用和执行时间。此外,pysnooper还支持日志输出、线程调试和自定义对象格式。这两个工具极大地提升了Python代码的调试效率。
摘要由CSDN通过智能技术生成

1.icecream简介

这是一个调试输出插件,相比于print函数,它可以自动优雅地输出各种变量类型,它会输出变量的位置,它可以输出函数的值,它还可以将值输出写入日志中。

通过引入icecream包,我们就可以在程序中更加简单地优雅地输出调试变量和函数的值,但是它和print函数一样,没有解决调试的侵入性问题,会产生调试代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zy5jD28U-1616489059061)(https://i.loli.net/2021/03/23/R2DJ8cIiVSn6QbC.png)]

1.1安装

pip install icecream

1.2导入模块包

from icecream import ic

1.3调试
  • 调试变量
from icecream import ic

a = "测试"
b = 123
ic(a)
ic(b)

ic| a: ‘测试’
ic| b: 123

  • 调试函数
from icecream import ic


def test01(a: int, b: int):
    return max(a, b)


ic(test01(5, 81))

ic| test01(5, 81): 81

  • 如果你想要找到执行代码的位置
def hello(user):
    if user:
        ic()
    else:
        ic()


ic(hello("heel"))

ic| icecream_lianx.py:19 in hello() at 05:51:43.987
ic| hello(“heel”): None

  • 自定义输出前缀
from icecream import ic
import time
from datetime import datetime


def time_format():
    return f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}|> '

# 通过设置ic对象中的 configureOutput 函数中的prefix参数,将prefix设置为自己定义的函数即可
ic.configureOutput(prefix=time_format)

for _ in range(3):
    time.sleep(1)
    ic('Hello')

2021-03-23 13:54:59|> ‘Hello’
2021-03-23 13:55:00|> ‘Hello’
2021-03-23 13:55:01|> ‘Hello’

  • 执行代码所在的行或者是代码所在的文件
from icecream import ic


def my_func(string):
    return string + "学习Python"


ic.configureOutput(includeContext=True)
ic(my_func("测试"))

2021-03-23 14:00:05|> icecream_lianx.py:29 in
my_func(“测试”): ‘测试学习Python’

2.icecream源码解释

1.icecream的参数
def configureOutput(self, prefix=_absent, outputFunction=_absent,
                    argToStringFunction=_absent, includeContext=_absent):
  • prefix :输出的前缀,比如需要在控制台中输出执行的日期,需要编写一个日期格式的方法/变量接受,然后使用ic.configureOutput(prefix=所定义的变量)
  • outputFunction:修改输出函数
# 例1
def fun1(a):
    a = fun2(a)
    print(a)


def fun2(func1):
    return func1


ic.configureOutput(includeContext=True)
ic.configureOutput(outputFunction=fun1)
ic('logging')						# ic| icecream_lianx.py:41 in <module>- 'logging'


# 例2
import logging
from icecream import ic


def warn(s):
    logging.warning(s)				# 调用logging的

    
ic.configureOutput(includeContext=True)
ic.configureOutput(outputFunction=warn)
ic('logging')						# 输出:WARNING:root:ic| icecream_lianx.py:41 in <module>- 'logging'
  • argToStringFunction:转为字符串函数
from icecream import ic


def to_string(obj):
    # 判断obj是否为字符串格式,如果为字符串格式则返回字符串及长度
    if isinstance(obj, str):
        return '[!string %r with length %i!]' % (obj, len(obj))
    # 返回一个对象的 string 格式
    return repr(obj)


ic.configureOutput(argToStringFunction=to_string)
ic("hello python")					# ic| [!string 'hello python' with length 12!]
# ic(True)							# ic| True
  • includeContext:默认是False,如果为True,则将ic()调用的文件名、行号和父函数添加到ic()的输出中
def func():
    ic("test")


func()								# ic| icecream_lianx.py:26 in func()- 'test'
	

3.pysnooper模块

4.pysnooper简介

非侵入式地调试方式,无需写打印语句即可在控制台输出相关的日志信息

1.安装

pip install pysnooper

2.导入包

import pysnooper

conda install -c conda-forge pysnooper

5.简单实例

import pysnooper


@pysnooper.snoop()
def demo_func():
    dict_list = dict()
    dict_list["name"] = "dyf"
    dict_list["age"] = 18
    dict_list["gender"] = "female"

    return dict_list


demo_func()

Source path:… D:/Personal_Project/PySnooper.py
17:12:29.232884 call 28 def demo_func():
17:12:29.232884 line 29 dict_list = dict()
New var:… dict_list = {}
17:12:29.232884 line 30 dict_list[“name”] = “dyf”
Modified var:… dict_list = {‘name’: ‘dyf’}
17:12:29.232884 line 31 dict_list[“age”] = 18
Modified var:… dict_list = {‘name’: ‘dyf’, ‘age’: 18}
17:12:29.232884 line 32 dict_list[“gender”] = “female”
Modified var:… dict_list = {‘name’: ‘dyf’, ‘age’: 18, ‘gender’: ‘female’}
17:12:29.232884 line 34 return dict_list
17:12:29.232884 return 34 return dict_list
Return value:… {‘name’: ‘dyf’, ‘age’: 18, ‘gender’: ‘female’}
Elapsed time: 00:00:00.000000

  • 代码的片段、行号等信息,以及每一行代码是何时调用的?
  • 函数内局部变量的值如何变化的?何时新增了变量,何时修改了变量。
  • 函数的返回值是什么?
  • 运行函数消耗了多少时间?

详细使用

1.重定向到日志
import pysnooper


@pysnooper.snoop(output='../log/debug.log')
def demo_func():
    dict_list = dict()
    dict_list["name"] = "dyf"
    dict_list["age"] = 18
    dict_list["gender"] = "female"

    return dict_list


demo_func()

就会输出对应的文件夹日志中

2.跟踪非局部变量值

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

import pysnooper

out = {"foo": "bar"}


@pysnooper.snoop(watch='out["foo"]')
def demo_func():
    dict_list = dict()
    dict_list["name"] = "dyf"
    dict_list["age"] = 18
    dict_list["gender"] = "female"

    return dict_list


demo_func()

跟踪非局部变量值.png

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

@pysnooper.snoop(watch_explode=('foo', 'bar'))
def demo_func():
    
控制台不会输出关于foo和bar的执行过程    
3.设置跟踪函数的深度

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

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

@pysnooper.snoop(depth=2)
# 跟踪函数中调用其他函数的执行过程,输出
4.设置调试日志的前缀

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

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

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

# 输出
demo_func: Source path:... D:/Personal_Project/Basic_learning/每天一练/PySnooper.py
demo_func: 18:07:12.108558 call        33 def demo_func():
demo_func: 18:07:12.109521 line        34     dict_list = dict()
demo_func: New var:....... dict_list = {}
demo_func: 18:07:12.109521 line        35     dict_list["name"] = "dyf"
demo_func: Modified var:.. dict_list = {'name': 'dyf'}
demo_func: 18:07:12.109521 line        36     dict_list["age"] = 18
demo_func: Modified var:.. dict_list = {'name': 'dyf', 'age': 18}
demo_func: 18:07:12.110554 line        37     dict_list["gender"] = "female"
demo_func: Modified var:.. dict_list = {'name': 'dyf', 'age': 18, 'gender': 'female'}
demo_func: 18:07:12.110554 line        39     return dict_list
demo_func: 18:07:12.111574 return      39     return dict_list
demo_func: Return value:.. {'name': 'dyf', 'age': 18, 'gender': 'female'}
demo_func: Elapsed time: 00:00:00.003016
5.设置最大的输出长度

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

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

@pysnooper.snoop(max_variable_length=200# 限制长度为200
@pysnooper.snoop(max_variable_length=None# 不限制长度
6.支持多线程调试模式

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

7.自定义对象的格式输出
# 【第一种写法】
@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():
import pysnooper

out = {"foo": "bar"}


class Person:
    pass


def print_person_obj(obj):
    return f"{obj.name} {obj.age} {obj.gender}>"


# @pysnooper.snoop(watch=('out["foo"]', 'foo.bar', 'self.foo["bar"]'))
# @pysnooper.snoop(watch_explode=('foo', 'bar'))
# @pysnooper.snoop(depth=2)
@pysnooper.snoop(thread_info=True)
@pysnooper.snoop(output="../log/debug.log", prefix="demo_func: ")
@pysnooper.snoop(custom_repr=(Person, print_person_obj))
def demo_func():
    dict_list = dict()
    dict_list["name"] = "dyf"
    dict_list["age"] = 18
    dict_list["gender"] = "female"

    return dict_list


demo_func()

Source path:… D:\Software\Anaconda\envs\Basic_learning\lib\site-packages\pysnooper\tracer.py
Starting var:… args = ()
Starting var:… kwargs = {}
Starting var:… function = <function demo_func at 0x0000020DDBA215E8>
Starting var:… self = <pysnooper.tracer.Tracer object at 0x0000020DDB8CCA48>
18:16:03.310906 23284-MainThread call 261 def simple_wrapper(*args, **kwargs):
18:16:03.310906 23284-MainThread line 262 with self:
18:16:03.311906 23284-MainThread line 263 return function(*args, **kwargs)
Source path:… D:/Personal_Project/PySnooper.py
18:16:03.312907 call 43 def demo_func():
18:16:03.312907 line 44 dict_list = dict()
New var:… dict_list = {}
18:16:03.312907 line 45 dict_list[“name”] = “dyf”
Modified var:… dict_list = {‘name’: ‘dyf’}
18:16:03.312907 line 46 dict_list[“age”] = 18
Modified var:… dict_list = {‘name’: ‘dyf’, ‘age’: 18}
18:16:03.312907 line 47 dict_list[“gender”] = “female”
Modified var:… dict_list = {‘name’: ‘dyf’, ‘age’: 18, ‘gender’: ‘female’}
18:16:03.312907 line 49 return dict_list
18:16:03.312907 return 49 return dict_list
Return value:… {‘name’: ‘dyf’, ‘age’: 18, ‘gender’: ‘female’}
Elapsed time: 00:00:00.000000
18:16:03.313961 23284-MainThread return 263 return function(*args, **kwargs)
Return value:… {‘name’: ‘dyf’, ‘age’: 18, ‘gender’: ‘female’}
Elapsed time: 00:00:00.003055

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值