Python pprint 输出数据结构竟如此优雅

百家 作者:AI100 2022-06-06 20:02:11

作者 | 云朵君

来源 | 数据STUDIO

你还在使用 print() 打印结果么?今天,云朵君将和大家一起探索 pprint 模块在 Python 中的主要用途,以及使用 pprint() 和 PrettyPrinter 的一些方法。看完今天文章,我彻底放弃使用 print() 了。

处理数据对任何Python数据科学家来说都是必不可少的,但有时这些数据并不漂亮。其实计算机不关心格式是什么样子的,但如果没有好的格式,很有可能会影响我们的阅读体验。当我们使用 print() 打印大字典或长列表时,其结果并不漂亮,虽然它很有效,但真的一点都不优雅。

Python 中的 pprint 模块是一个实用模块,你可以用它来以一种可读的、漂亮的方式打印数据结构。它是标准库的一部分,对于调试处理 API 请求、大型 JSON 文件和一般数据的代码特别有用。

接下来我们主要学习:

  • 理解 pprint 模块的优势

  • 了解如何使用 pprint()PrettyPrinter 及它们的参数

  • 能够创建我们自己的 PrettyPrinter 实例

  • 保存格式化的字符串输出而不是打印它

  • 打印和识别递归数据结构

pprint 模块的优势

Python pprint 模块在很多情况下都有帮助。在进行 API 请求,处理JSON文件,或者处理复杂的嵌套数据时,它都能派上用场。你可能会发现,使用普通的 print() 函数并不足以有效地探索你的数据和调试你的应用程序。当你对字典和列表使用 print() 时,输出不包含任何新行。

为了演示,我们获取一些样例数据,它是一个json数据结构的数据,用一般 print() 函数打印结果看看。

>>> print(users)
[{'address': {'city''chengdu''geo': {'lat''102.54''lng''30.05'}}, 'email''yunduojun@study.com''id'1'name''数据STUDIO''WeChat''Mr_cloud_data''authorname''yunduojun''website''http://mp.weixin.qq.com/'}, {'address': {'city''chengdu''geo': {'lat''102.54''lng''30.05'}}, 'email''xiaohouzi@study.com''id'1'name''机器学习研习院''WeChat''Mr_cloud_data''authorname''xiaohouzi''website''http://mp.weixin.qq.com/'}]

天哪!一个巨大的行,并且没有换行。根据你的控制台设置,这可能显示为一个很长的行。另外,我们的控制台输出可能开启了包字模式,这是最常见的情况。不幸的是,这并不能使输出变得更友好。

如果我们只需要获取第一个和最后一个用户数据。首先分析这似乎是一个列表,可能会想到写一个循环来打印这些项目。

for user in users:
    print(user)

这个 for 循环会将每个对象打印在一个单独的行上,但即使如此,每个对象所占的空间也远远超过了单行所能容纳的。这样的打印方式确实让事情变得更好一些,但这绝不是理想的。上面的例子是一个相对简单的数据结构,但是对于一个深度嵌套的字典,你会怎么做?

当然,我们可以写一个使用递归的函数来找到打印一切的方法。不幸的是,我们很可能会遇到一些边缘情况,在这些情况下这是行不通的。我们甚至会发现自己写了一整个模块的函数,只是为了掌握数据的结构。

接下来我们开始进入pprint模块的学习!

使用 pprint

前面已经提到, pprint 是一个 Python 模块,以一种漂亮的方式打印数据结构。它早就是 Python 内置标准库的一部分,所以没有必要单独安装。只需要导入它的 pprint() 函数。

from pprint import pprint

然后,不要像上面的例子那样采用一般的 print(users) 方法,可以调用这个优雅的函数 pprint(users) 来使输出更加美观。

这个函数以一种新的和改进的 pretty 的方式打印用户数据。

pprint(users)

[{'WeChat': 'Mr_cloud_data',
'address': {'city': 'chengdu', 'geo': {'lat': '102.54', 'lng': '30.05'}},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'},
{'WeChat': 'Mr_cloud_data',
'address': {'city': 'chengdu', 'geo': {'lat': '102.54', 'lng': '30.05'}},
'authorname': '小猴子',
'email': 'xiaohouzi@study.com',
'id': 10,
'name': '机器学习研习院',
'website': 'http://mp.weixin.qq.com/'}]

为了演示结果更加便于阅读,我们截取其中一小段。

这个输出结构相当漂亮!字典的键以缩进的格式展示出来,这样的输出使得在直观上分析数据结构变得更加直接了当。

如果你是一个喜欢尽可能少打字的人,可以通过使用别名来使用 pprint(),即 pp()

from pprint import pp
pp(users)

pp()只是pprint()的一个包装,它的行为完全相同。

注意:Python3.8.0 alpha 2版本[1]开始包含这个别名。

然而,即使是默认的输出,也可能会有太多的信息需要扫描。接下来对输出进行一些调整。我们可以向 pprint() 传递各种参数,使最复杂的数据结构也变得漂亮。

pprint() 的可选参数

接下来我们将一起了解 pprint() 的所有可用参数。这里共有七个参数,可以用来配置我们的 Pythonic pretty printer。这些参数没有必要全部使用,我们可以根据需求选用其中最有价值的那几个参数即可。然而,我们发现其中最有价值的一个参数非深度depth莫属了。

数据概括:depth

其中一个最方便的参数是深度depth。下面的Python命令只有在数据结构处于或低于指定的深度时才会打印用户的全部内——当然是在保持美观的情况下。更深的数据结构的内容被替换成三个点。

pprint( users, depth=1)

[{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}]

从结果可以看出,这确实是一个字典的列表。为了进一步探索这个数据结构,可以把深度增加一级,这将打印出用户中所有字典的顶层键。

[{'WeChat': 'Mr_cloud_data',
'address': {...},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'},
{'WeChat': 'Mr_cloud_data',
'address': {...},
'authorname': '小猴子',
'email': 'xiaohouzi@study.com',
'id': 10,
'name': '机器学习研习院',
'website': 'http://mp.weixin.qq.com/'}]

现在我们可以快速检查所有的字典是否共享它们的顶层键。这个观察结果很有价值,特别是如果我们的任务是开发一个消耗这样的数据的应用程序。

提供数据空间:indent

缩进indent参数控制每一级 pretty-printed 表述在输出中的缩进程度。默认的缩进是1,即一个空格字符。

pprint(users[0], depth=1)

{'WeChat': 'Mr_cloud_data',
'address': {...},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'}

pprint(users[0], depth=1, indent=4)

{ 'WeChat': 'Mr_cloud_data',
'address': {...},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'}

pprint() 的缩进行为中最重要的部分是保持所有的键在视觉上对齐,缩进的程度取决于缩进参数和键的位置。

由于上面的例子中没有嵌套,缩进量完全基于缩进参数。在这两个例子中,请注意开头的大括号({)是如何被算作第一个键的缩进单位的。在第一个例子中,第一个键的开头单引号就在{之后,中间没有任何空格,因为缩进被设置为1。

然而,当有嵌套时,缩进被应用于第一个元素的行内,然后pprint()使所有后续元素与第一个元素保持一致。因此,如果在打印用户时将缩进设置为4,第一个元素将被缩进4个字符,而嵌套的元素将被缩进8个以上的字符,因为缩进是从第一个键的末端开始的。

pprint(users[0], depth=2, indent=4)

{ 'WeChat': 'Mr_cloud_data',
'address': {'city': 'chengdu', 'geo': {...}},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'}

限制行长度:width

默认情况下,pprint() 每行最多只能输出80个字符。可以通过传递一个宽度参数来定制这个值。pprint() 将努力把内容放在一行中。如果一个数据结构的内容超过了这个限制,那么它将把当前数据结构的每一个元素都打印在新的一行。

pprint(users[0])

{'WeChat': 'Mr_cloud_data',
'address': {'city': 'chengdu', 'geo': {'lat': '102.54', 'lng': '30.05'}},
'authorname': '云朵君',
'email': 'yunduojun@study.com',
'id': 1,
'name': '数据STUDIO',
'website': 'http://mp.weixin.qq.com/'}

当把宽度保持在默认的 80 个字符时,在users[0]['address']['geo'] 处的字典只包含一个'lat'和一个 'lng' 属性。这意味着将缩进和打印出字典所需的字符数(包括中间的空格)相加,不到八十个字符。由于少于80个字符,默认的宽度,pprint() 把它全部放在一行。

然而,如果字典会超过默认的宽度,所以 pprint() 将每个键放在一个新行上。这对于字典、列表、元组和集合都是如此。

如果把宽度设置成一个大的数值,如160,那么所有嵌套的字典都可以放在一行。甚至可以把它做到极致,使用500这样一个巨大的值,对于这个例子来说,它把整个字典打印在一行上。

>>> pprint( users[0], width=500)
{'WeChat''Mr_cloud_data''address': {'city''chengdu''geo': {'lat''102.54''lng''30.05'}}, 'authorname''云朵君''email''yunduojun@study.com''id'1'name''数据STUDIO''website''http://mp.weixin.qq.com/'}

在这里,我们得到了将宽度设置为一个相对较大的值的效果。我们可以反其道而行之,将宽度设置为一个较低的值,如1。然而,这样做的主要效果是确保每个数据结构都能在不同的行上显示其组件。我们仍然会得到视觉上的缩进,将组件排成一行。

>>> pprint( users[0], width=5)

{'WeChat''Mr_cloud_data',
 'address': {'city''chengdu',
             'geo': {'lat''102.54',
                     'lng''30.05'}},
 'authorname''云朵君',
 'email''yunduojun@study.com',
 'id'1,
 'info': {'introduction''

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接