pprint
icecream

说到调试,主流IDE都提供了强大调试功能。而且python也提供了强大的日志模块如loggingloguru,调试模块 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

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'

参考