8.11. "pprint" --- 数据美化输出
*******************************

**源代码：** Lib/pprint.py

======================================================================

"pprint" 模块提供了“美化打印”任意 Python 数据结构的功能，这种美化形式
可用作对解释器的输入。 如果经格式化的结构包含非基本 Python 类型的对象
，则其美化形式可能无法被加载。 包含文件、套接字或类对象，以及许多其他
不能用 Python 字面值来表示的对象都有可能导致这样的结果。

格式化后的形式会在可能的情况下以单行来表示对象，并在无法在允许宽度内容
纳对象的情况下将其分为多行。 如果你需要调整宽度限制则应显式地构造
"PrettyPrinter" 对象。

Dictionaries are sorted by key before the display is computed. 字典在
被计算前通过键值来排序。

"pprint" 模块定义了一个类：

class pprint.PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, compact=False)

   构造一个 "PrettyPrinter" 实例。 此构造器接受几个关键字形参。 使用
   *stream* 关键字可设置输出流；在流对象上使用的唯一方法是文件协议的
   "write()" 方法。 如果未指定此关键字，则 "PrettyPrinter" 会选择
   "sys.stdout"。 每个递归层级的缩进量由 *indent* 指定；默认值为一。
   其他值可导致输出看起来有些怪异，但可使得嵌套结构更易区分。 可被打印
   的层级数量由 *depth* 控制；如果数据结构的层级被打印得过深，其所包含
   的下一层级会被替换为 "..."。 在默认情况下，对被格式化对象的层级深度
   没有限制。 希望的输出宽度可使用 *width* 形参来限制；默认值为 80 个
   字符。 如果一个结构无法在限定宽度内被格式化，则将做到尽可能接近。
   如果 *compact* 为假值（默认）则长序列的每一项将被格式化为单独的行。
   如果 *compact* 为真值，则格式化将在 *width* 可容纳的情况下把尽可能
   多的项放入每个输出行。

   在 3.4 版更改: 加入*compact* 参数。

   >>> import pprint
   >>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
   >>> stuff.insert(0, stuff[:])
   >>> pp = pprint.PrettyPrinter(indent=4)
   >>> pp.pprint(stuff)
   [   ['spam', 'eggs', 'lumberjack', 'knights', 'ni'],
       'spam',
       'eggs',
       'lumberjack',
       'knights',
       'ni']
   >>> pp = pprint.PrettyPrinter(width=41, compact=True)
   >>> pp.pprint(stuff)
   [['spam', 'eggs', 'lumberjack',
     'knights', 'ni'],
    'spam', 'eggs', 'lumberjack', 'knights',
    'ni']
   >>> tup = ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead',
   ... ('parrot', ('fresh fruit',))))))))
   >>> pp = pprint.PrettyPrinter(depth=6)
   >>> pp.pprint(tup)
   ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', (...)))))))

"pprint" 模块还提供了一些快捷函数：

pprint.pformat(object, indent=1, width=80, depth=None, *, compact=False)

   将 *object* 格式化表示作为字符串返回。 *indent*, *width*, *depth*
   和 *compact* 将作为格式化形参被传入 "PrettyPrinter" 构造器。

   在 3.4 版更改: 加入*compact* 参数。

pprint.pprint(object, stream=None, indent=1, width=80, depth=None, *, compact=False)

   在 *stream* 上打印 *object* 的格式化表示，并附带一个换行符。 如果
   *stream* 为 "None"，则使用 "sys.stdout"。 这可以替换 "print()" 函数
   在交互式解释器中使用以查看值（你甚至可以执行重新赋值 "print =
   pprint.pprint" 以在特定作用域中使用）。 *indent*, *width*, *depth*
   和 *compact* 将作为格式化形参被传给 "PrettyPrinter" 构造器。

   在 3.4 版更改: 加入*compact* 参数。

   >>> import pprint
   >>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
   >>> stuff.insert(0, stuff)
   >>> pprint.pprint(stuff)
   [<Recursion on list with id=...>,
    'spam',
    'eggs',
    'lumberjack',
    'knights',
    'ni']

pprint.isreadable(object)

   确定 *object* 的格式化表示是否“可读”，或是否可被用来通过 "eval()"
   重新构建对象的值。 此函数对于递归对象总是返回 "False"。

   >>> pprint.isreadable(stuff)
   False

pprint.isrecursive(object)

   确定 *object* 是否需要递归表示。

此外还定义了一个支持函数：

pprint.saferepr(object)

   返回 *object* 的字符串表示，并为递归数据结构提供保护。 如果
   *object* 的表示形式公开了一个递归条目，该递归引用会被表示为
   "<Recursion on typename with id=number>"。 该表示因而不会进行其它格
   式化。

   >>> pprint.saferepr(stuff)
   "[<Recursion on list with id=...>, 'spam', 'eggs', 'lumberjack', 'knights', 'ni']"


8.11.1. PrettyPrinter 对象
==========================

"PrettyPrinter" 的实例具有下列方法：

PrettyPrinter.pformat(object)

   返回 *object* 格式化表示。 这会将传给 "PrettyPrinter" 构造器的选项
   纳入考虑。

PrettyPrinter.pprint(object)

   在所配置的流上打印 *object* 的格式化表示，并附加一个换行符。

下列方法提供了与同名函数相对应的实现。 在实例上使用这些方法效率会更高
一些，因为不需要创建新的 "PrettyPrinter" 对象。

PrettyPrinter.isreadable(object)

   确定对象的格式化表示是否“可读”，或者是否可使用 "eval()" 重建对象值
   。 请注意此方法对于递归对象将返回 "False"。 如果设置了
   "PrettyPrinter" 的 *depth* 形参并且对象深度超出允许范围，此方法将返
   回 "False"。

PrettyPrinter.isrecursive(object)

   确定对象是否需要递归表示。

此方法作为一个钩子提供，允许子类修改将对象转换为字符串的方式。 默认实
现使用 "saferepr()" 实现的内部方式。

PrettyPrinter.format(object, context, maxlevels, level)

   返回三个值：字符串形式的 *object* 已格式化版本，指明结果是否可读的
   旗标，以及指明是否检测到递归的旗标。 第一个参数是要表示的对象。 第
   二个是以对象 "id()" 为键的字典，这些对象是当前表示上下文的一部分（
   影响 *object* 表示的直接和间接容器）；如果需要呈现一个已经在
   *context* 中表示的对象，则第三个返回值应当为 "True"。 对 "format()"
   方法的递归调用应当将容器的附加条目添加到此字典中。 第三个参数
   *maxlevels* 给出了对递归的请求限制；如果没有请求限制则其值将为 "0"
   。 此参数应当不加修改地传给递归调用。 第四个参数 *level* 给出于当前
   层级；传给递归调用的参数值应当小于当前调用的值。


8.11.2. 示例
============

为了演示 "pprint()" 函数及其形参的几种用法，让我们从 PyPI 获取关于某个
项目的信息:

   >>> import json
   >>> import pprint
   >>> from urllib.request import urlopen
   >>> with urlopen('https://pypi.org/pypi/sampleproject/json') as resp:
   ...     project_info = json.load(resp)['info']

"pprint()" 以其基本形式显示了整个对象:

   >>> pprint.pprint(project_info)
   {'author': 'The Python Packaging Authority',
    'author_email': 'pypa-dev@googlegroups.com',
    'bugtrack_url': None,
    'classifiers': ['Development Status :: 3 - Alpha',
                    'Intended Audience :: Developers',
                    'License :: OSI Approved :: MIT License',
                    'Programming Language :: Python :: 2',
                    'Programming Language :: Python :: 2.6',
                    'Programming Language :: Python :: 2.7',
                    'Programming Language :: Python :: 3',
                    'Programming Language :: Python :: 3.2',
                    'Programming Language :: Python :: 3.3',
                    'Programming Language :: Python :: 3.4',
                    'Topic :: Software Development :: Build Tools'],
    'description': 'A sample Python project\n'
                   '=======================\n'
                   '\n'
                   'This is the description file for the project.\n'
                   '\n'
                   'The file should use UTF-8 encoding and be written using '
                   'ReStructured Text. It\n'
                   'will be used to generate the project webpage on PyPI, and '
                   'should be written for\n'
                   'that purpose.\n'
                   '\n'
                   'Typical contents for this file would include an overview of '
                   'the project, basic\n'
                   'usage examples, etc. Generally, including the project '
                   'changelog in here is not\n'
                   'a good idea, although a simple "What\'s New" section for the '
                   'most recent version\n'
                   'may be appropriate.',
    'description_content_type': None,
    'docs_url': None,
    'download_url': 'UNKNOWN',
    'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1},
    'home_page': 'https://github.com/pypa/sampleproject',
    'keywords': 'sample setuptools development',
    'license': 'MIT',
    'maintainer': None,
    'maintainer_email': None,
    'name': 'sampleproject',
    'package_url': 'https://pypi.org/project/sampleproject/',
    'platform': 'UNKNOWN',
    'project_url': 'https://pypi.org/project/sampleproject/',
    'project_urls': {'Download': 'UNKNOWN',
                     'Homepage': 'https://github.com/pypa/sampleproject'},
    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
    'requires_dist': None,
    'requires_python': None,
    'summary': 'A sample Python project',
    'version': '1.2.0'}

结果可以被限制到特定的 *depth* (更深层的内容将使用省略号):

   >>> pprint.pprint(project_info, depth=1)
   {'author': 'The Python Packaging Authority',
    'author_email': 'pypa-dev@googlegroups.com',
    'bugtrack_url': None,
    'classifiers': [...],
    'description': 'A sample Python project\n'
                   '=======================\n'
                   '\n'
                   'This is the description file for the project.\n'
                   '\n'
                   'The file should use UTF-8 encoding and be written using '
                   'ReStructured Text. It\n'
                   'will be used to generate the project webpage on PyPI, and '
                   'should be written for\n'
                   'that purpose.\n'
                   '\n'
                   'Typical contents for this file would include an overview of '
                   'the project, basic\n'
                   'usage examples, etc. Generally, including the project '
                   'changelog in here is not\n'
                   'a good idea, although a simple "What\'s New" section for the '
                   'most recent version\n'
                   'may be appropriate.',
    'description_content_type': None,
    'docs_url': None,
    'download_url': 'UNKNOWN',
    'downloads': {...},
    'home_page': 'https://github.com/pypa/sampleproject',
    'keywords': 'sample setuptools development',
    'license': 'MIT',
    'maintainer': None,
    'maintainer_email': None,
    'name': 'sampleproject',
    'package_url': 'https://pypi.org/project/sampleproject/',
    'platform': 'UNKNOWN',
    'project_url': 'https://pypi.org/project/sampleproject/',
    'project_urls': {...},
    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
    'requires_dist': None,
    'requires_python': None,
    'summary': 'A sample Python project',
    'version': '1.2.0'}

此外，还可以设置建议的最大字符 *width*。 如果一个对象无法被拆分，则将
超出指定宽度:

   >>> pprint.pprint(project_info, depth=1, width=60)
   {'author': 'The Python Packaging Authority',
    'author_email': 'pypa-dev@googlegroups.com',
    'bugtrack_url': None,
    'classifiers': [...],
    'description': 'A sample Python project\n'
                   '=======================\n'
                   '\n'
                   'This is the description file for the '
                   'project.\n'
                   '\n'
                   'The file should use UTF-8 encoding and be '
                   'written using ReStructured Text. It\n'
                   'will be used to generate the project '
                   'webpage on PyPI, and should be written '
                   'for\n'
                   'that purpose.\n'
                   '\n'
                   'Typical contents for this file would '
                   'include an overview of the project, '
                   'basic\n'
                   'usage examples, etc. Generally, including '
                   'the project changelog in here is not\n'
                   'a good idea, although a simple "What\'s '
                   'New" section for the most recent version\n'
                   'may be appropriate.',
    'description_content_type': None,
    'docs_url': None,
    'download_url': 'UNKNOWN',
    'downloads': {...},
    'home_page': 'https://github.com/pypa/sampleproject',
    'keywords': 'sample setuptools development',
    'license': 'MIT',
    'maintainer': None,
    'maintainer_email': None,
    'name': 'sampleproject',
    'package_url': 'https://pypi.org/project/sampleproject/',
    'platform': 'UNKNOWN',
    'project_url': 'https://pypi.org/project/sampleproject/',
    'project_urls': {...},
    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
    'requires_dist': None,
    'requires_python': None,
    'summary': 'A sample Python project',
    'version': '1.2.0'}
