文章目录
1.icecream简介
这是一个调试输出插件,相比于print函数,它可以自动优雅地输出各种变量类型,它会输出变量的位置,它可以输出函数的值,它还可以将值输出写入日志中。
通过引入icecream包,我们就可以在程序中更加简单地优雅地输出调试变量和函数的值,但是它和print函数一样,没有解决调试的侵入性问题,会产生调试代码
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()
和 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