【Python 雑談・雑学 + coding challenge】Python の pprint 機能を自分で実装してみよう! 自分なりの Pretty Print できちゃいます!! 投稿一覧へ戻る

Tags: Python , miscellaneous , challenge , pprint , pretty print

Published 2020年8月19日8:17 by T.Tsuyoshi

Python では pprint モジュールが提供されていて、ネストしているデータ構造を階層的に出力してくれます。


from pprint import pprint


a = [1, 2, 3, 4, 5]
b = [1, [2, [5, 6], 3], 4]


pprint(a, indent=4, width=15)

# [1, 2, 3, 4, 5]




pprint(b, indent=4, width=15)

# [ 1,
# [ 2,
# [5, 6],
# 3],
# 4]




問題 ( 制限時間: 60 分 ):


pprint と同様の機能の実装です ( pprint() を使っちゃダメですよ )。


ただし、実装を簡略化するために、list から成るネスト構造だけを対象にします。


出力は以下の通りです。


a = [1, 2, 3, 4, 5]

my_pretty_print(a)

# [1, 2, 3, 4, 5]




b = [1, [2, [5, 6], 3], 4]

my_pretty_print(b)

# [
# 1,
# [
# 2,
# [5, 6],
# 3
# ],
# 4
# ]




c = [1, 2, [3, 4, [5, 6]]]

my_pretty_print(c)

# [
# 1,
# 2,
# [
# 3,
# 4,
# [5, 6]
# ]
# ]




d = [1, 'Nana', ['Saki', 4, 5, [6, [7, 'Yuka'], 'Hana']], 10, ['Megu', 12]]

my_pretty_print(d)

# [
# 1,
# Nana,
# [
# Saki,
# 4,
# 5,
# [
# 6,
# [7, 'Yuka'],
# Hana
# ]
# ],
# 10,
# ['Megu', 12]
# ]




当たり前ですが、要素を区切るカンマ ',' の位置と数は元の構造と同じでなければなりません。



さていかがだったでしょうか?


今回の実装では list のネスト構造だけを対象にしています。


他のデータ構造が混じっていても対応できるように是非ご自分で進化させてみてください。


以下のように実装してみました。


my_pretty_print() はネストが深くなっていますので、まだまだ改良の余地が多いです。


また、コメントを多めに入れたのでコード本体が見辛くなってます、ご了承ください。


def print_line(item, level, indent=4, suffix=''):
"""
データを出力します

出力するデータの直前に、indent で指定された数のスペースを level で指定された回数だけ繰り返したものを付加します。
(index = 4, level = 2 であればスペースが 4 * 2 = 8 個付加されます。level が 0 のときスペースは付加しません)

出力するデータの直後に、suffix を付加して出力します。

:param item: 出力データ
:param level: 出力データの直前に付加する (indent * スペース) の繰り返し回数
:param indent: インデント数
:param suffix: 出力データの直後に付加する文字列
"""

spaces = '' if level == 0 else (' ' * indent) * level
print(f"{spaces}{item}{suffix}")


def check_iterable_not_str(item):
"""
データがイテラブルであるか判定します。
ただし、イテラブルであっても文字列である場合は False を返します。
(この例題では list だけをターゲットにしていますが、この関数では str 以外のイテラブルであれば True を返しています)
"""

return hasattr(item, '__iter__') and not isinstance(item, str)


def my_pretty_print(seq, level=0, indent=4, suffix=''):
"""
与えられたシーケンスを見やすいように階層表示します。

:param seq: シーケンス。この例題では階層を構成する全てのシーケンスがリストでなければなりません。
:param level: この階層の出力を始めるインデントレベル。level * indent 分のスペースを挿入します。
:param indent: インデント数
:param suffix: この関数に渡されてきたシーケンスの末尾に付加する文字列
"""


# 与えられたデータがシーケンスであるか確認します
if check_iterable_not_str(seq):
# 与えられたシーケンスの中にさらにシーケンスがある場合は「階層表示」します
if [item for item in seq if check_iterable_not_str(item)]: # シーケンス中にシーケンスが存在するかを確認するためだけの list comprehension
print_line('[', level, indent)
for index, item in enumerate(seq, 1):
# 渡されてきたシーケンス内の各要素末尾に付加する文字を設定します
# シーケンス内の最後の要素以外には ',' を付加します
inner_suffix = '' if index == len(seq) else ','

# 要素がシーケンスであれば再帰処理します
if check_iterable_not_str(item):
my_pretty_print(item, level + 1, indent, inner_suffix)
else:
print_line(item, level + 1, indent, inner_suffix)
# このシーケンスの処理の最後に ']' を付加します
# このとき、']' の後に付加する suffix は、このシーケンスを要素として含む 1 つ上の階層内での位置を反映するための、渡されてきた suffix です
print_line(']', level, indent, suffix)
# 与えられたシーケンスが他のシーケンスを含まない場合はそのまま表示します
else:
print_line(seq, level, indent, suffix)

この投稿をメールでシェアする

0 comments

コメントはまだありません。

コメントを追加する(不適切と思われるコメントは削除する場合があります)