8.11. pprint --- データ出力の整然化

ソースコード: Lib/pprint.py


pprint モジュールを使うと、Pythonの任意のデータ構造をインタープリタへの入力で使われる形式にして "pretty-print" できます。書式化された構造の中にPythonの基本的なタイプではないオブジェクトがあるなら、表示できないかもしれません。表示できないのは、ファイル、ソケット、あるいはクラスのようなオブジェクトや、 その他Pythonのリテラルとして表現できない様々なオブジェクトが含まれていた場合です。

可能であればオブジェクトを1行で整形しますが、与えられた幅に合わないなら複数行に分けて整形します。 出力幅を指定したい場合は、 PrettyPrinter オブジェクトを作成して明示してください。

辞書は表示される前にキーの順でソートされます。

pprint モジュールには1つのクラスが定義されています:

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

PrettyPrinter インスタンスを作ります。このコンストラクタにはいくつかのキーワード引数を設定できます。 stream キーワードで出力ストリームを設定できます;このストリームに対して呼び出されるメソッドはファイルプロトコルの write() メソッドだけです。もし設定されなければ、 PrettyPrintersys.stdout を使用します。再帰的なレベルごとに加えるインデントの量は indent で設定できます;デフォルト値は1です。他の値にすると出力が少しおかしく見えますが、ネスト化されたところが見分け易くなります。出力されるレベルは 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 を書式化して文字列として返します。 indentwidth depth と、 compactPrettyPrinter コンストラクタに書式化引数として渡されます。

バージョン 3.4 で変更: compact 引数が追加されました。

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

stream 上に object の書式化された表現、続いて改行を出力します。 streamNone の場合、 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 を書式化して出力できる("readable") か、あるいは eval() を使って値を再構成できるかを返します。再帰的なオブジェクトに対しては常に False を返します。

>>> pprint.isreadable(stuff)
False
pprint.isrecursive(object)

object が再帰的な表現かどうかを返します。

さらにもう1つ、関数が定義されています:

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)

object を書式化して出力できる("readable")か、あるいは eval() を使って値を再構成できるかを返します。これは再帰的なオブジェクトに対して False を返すことに注意して下さい。もし PrettyPrinterdepth 引数が設定されていて、オブジェクトのレベルが設定よりも深かったら、 False を返します。

PrettyPrinter.isrecursive(object)

オブジェクトが再帰的な表現かどうかを返します。

このメソッドをフックとして、サブクラスがオブジェクトを文字列に変換する方法を修正するのが可能になっています。デフォルトの実装では、内部で saferepr() を呼び出しています。

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

次の3つの値を返します。object をフォーマット化して文字列にしたもの、その結果が読み込み可能かどうかを示すフラグ、再帰が含まれているかどうかを示すフラグ。最初の引数は表示するオブジェクトです。 2つめの引数はオブジェクトの id() をキーとして含むディクショナリで、オブジェクトを含んでいる現在の(直接、間接に object のコンテナとして表示に影響を与える)環境です。ディクショナリ context の中でどのオブジェクトが表示されたか表示する必要があるなら、3つめの返り値は True になります。 format() メソッドの再帰呼び出しではこのディクショナリのコンテナに対してさらにエントリを加えます。 3つめの引数 maxlevels で再帰呼び出しのレベルを制限します。制限しない場合、 0 になります。この引数は再帰呼び出しでそのまま渡されます。 4つめの引数 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'}