pprint
icecream
说到调试,主流IDE都提供了强大调试功能。而且python也提供了强大的日志模块如logging、loguru,调试模块 pdb以及后起之秀 PySnooper等,但是这些都不是本文介绍的重点。
这里主要介绍的还是print大法,这也许是用的频率最高的调试方式了
pprint
pprint(pretty printer)标准库模块
官方文档
使用简介
print虽然可以打印,但如果是对复杂的数据结构(多层嵌套的列表、元组和字典等),就会是一大堆显示在控制台,不便于查看
from pprint import pprint
info = {'name': 'jerry', 'age': 28, 'salary': 1000000, 'friends': ['tom', 'lily', 'lucy']}
print(info)
pprint(info)
------输出结果------------
{'name': 'jerry', 'age': 28, 'salary': 1000000, 'friends': ['tom', 'lily', 'lucy']}
{'age': 28,
'friends': ['tom', 'lily', 'lucy'],
'name': 'jerry',
'salary': 1000000}
pprint(object, stream=None, indent=1, width=80, depth=None, *,compact=False, sort_dicts=True)
- stream:输出流对象,默认stream=None,那么输出流对象是sys.stdout
- indent:缩进空格数,默认是1
- width:行宽度参数,默认为80;当打印的字符(character)小于 80 时,pprint() 基本上等同于内置函数 print(),当字符超出时,它会作美化,进行格式化输出
- depth:设置打印的层级,默认为None即全打印
- compact:conpact为False(默认)则每一项将被格式化为单独的行。 为True时,则将在 width 可容纳的的情况下把尽可能多的项放入每个输出行
- sort_dicts:py3.8添加,为True时(默认)字典将被格式化为按key排序,否则将按插入顺序显示
from pprint import pprint
info = {'name': 'jerry', 'age': 28, 'salary': 1000000, 'friends': ['tom', 'lily', 'lucy']}
pprint(info, sort_dicts=False)
------- 输出结果-------------
{'name': 'jerry',
'age': 28,
'salary': 1000000,
'friends': ['tom', 'lily', 'lucy']}
- 更多其他方法可参看官方文档
icecream
另一个更强大的打印调试方式,Icecream是一个Python第三方库,可通过最少的代码使打印调试更清晰明了
官方文档
安装
pip3 install icecream
使用简介
# 有时候我们可能会这样来打印函数的返回值
def my_add(x, y):
return x + y
print('my_add(1,2):', my_add(1, 2))
显然不够优雅和灵活
icecream库的作用:
- 显示变量
from icecream import ic
def my_add(x, y):
return x + y
ic(my_add(1, 2))
------输出结果-----
ic| my_add(1, 2): 3
同样,对其他数据结构或类函数也同样适用
from icecream import ic
d = {'name': 'jerry', 'age': 28}
ic(d['name'])
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
return f"{self.name}'s age is {self.age}"
ic(Person('jerry', 28).show_info())
----------输出结果-----------
ic| d['name']: 'jerry'
ic| Person('jerry', 28).show_info(): "jerry's age is 28"
- 显示执行路径
from icecream import ic
# 有时候,我们会这样,在分支中加入一些特殊print来看看执行时进入哪个分支
def hello(name: str):
if name:
print('+++++++++')
return f'hello {name}'
else:
print('-----------')
return 'not a person'
# 对比如下函数执行后的信息
def hello_with_ic(name: str):
if name:
ic()
return f'hello {name}'
else:
ic()
return 'not a person'
print(hello('jerry'))
print()
print(hello_with_ic('jerry'))
----------输出结果------------
+++++++++
hello jerry
hello jerry
ic| t1.py:14 in hello_with_ic() at 13:26:56.774
ic配置
通过上面的简单介绍,发现ic有自己默认输出格式。如果觉得不符合你的要求,ic还提供configureOutput方法来设置自定义的格式
- configureOutput
- prefix参数:设置前缀,默认是
'ic| '
,可以是字符串或函数(名)(函数返回值必须是字符串) - outputFunction参数:设置输出函数,默认是stderr
- argToStringFunction参数:设置输出字符串格式,默认是PrettyPrint的pprint.pformat()(即上面介绍的pprint)
- includeContext参数:设置是否打印更多信息,包含代码执行的行和代码文件等,默认为False
- prefix参数:设置前缀,默认是
ic配置示例
设置前缀
# 例1:固定字符串
from icecream import ic
ic.configureOutput(prefix='test log --->')
ic('this is a test log')
ic('this is another test log')
------输出结果----------
test log --->'this is a test log'
test log --->'this is another test log'
# 例2: 函数(返回值必须是字符串)
from icecream import ic
import datetime
def prefix_current_time():
return f'{datetime.datetime.now()}-->'
ic.configureOutput(prefix=prefix_current_time)
ic('this is a test log')
ic('this is another test log')
-----------输出结果------------------
2021-01-26 00:27:11.317012-->'this is a test log'
2021-01-26 00:27:11.340800-->'this is another test log'
设置输出函数
from icecream import ic
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
def out(s):
return logging.info(s)
ic.configureOutput(prefix='test log--->', outputFunction=out)
ic('this is a test log')
ic('this is another test log')
---------------输出结果----------------
2021-01-26 00:33:10 test log--->'this is a test log'
2021-01-26 00:33:10 test log--->'this is another test log'
设置输出字符串格式
from icecream import ic
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f'<person:{self.name} {self.age}>'
def to_string(obj):
if isinstance(obj, str):
return f'[length of {obj} is {len(obj)}'
return repr(obj)
ic.configureOutput(prefix='test log--->', argToStringFunction=to_string)
ic('this is a test log')
ic('this is another test log')
ic(Person('jerry', 28))
--------输出结果------------------
test log--->'this is a test log': [length of this is a test log is 18
test log--->'this is another test log': [length of this is another test log is 24
test log--->Person('jerry', 28): <person:jerry 28>
设置是否输出更多信息
from icecream import ic
ic.configureOutput(prefix='test log--->', includeContext=True) # 注释本行,即不设置的情况下,对比输出结果
def foo_1():
return 'hello monkey'
def foo_2():
ic(foo_1())
foo_2()
-------输出结果------------
test log--->t1.py:9 in foo_2()- foo_1(): 'hello monkey'
# 不设置输出结果ic| foo_1(): 'hello monkey'