Python で日本語を含むリストやディクショナリの表示にもの思い

大学の友人が Python を学び始め、デバッグしていて抱いた疑問をググったところ、以下の解決方法を見つけたようです。

Pythonでコード書いてると、1回は残念だなぁと思うポイントとして表題の件があると思います。具体的には以下です。

# リストも辞書も出力がお世辞にも良いとは言えない。。
>>> print ['あ', 'い', 'う']
['\xe3\x81\x82', '\xe3\x81\x84', '\xe3\x81\x86']
>>> print {'title':'ねじまき鳥', 'author':'村上春樹'}
{'author': '\xe6\x9d\x91\xe4\xb8\x8a\xe6\x98\xa5\xe6\xa8\xb9', 'title': '\xe3\x81\xad\xe3\x81\x98\xe3\x81\xbe\xe3\x81\x8d\xe9\xb3\xa5'}

日本語がバイト表現な上、全要素が1行で表示されています。これではちょっとprintデバッグするにも萎えますよね。複雑な辞書を出力した場合なんかは、出力された文字列の整形にかなりのパワーを裂かれること請け合いです。

Pythonで日本語を含んだリストと辞書をpretty printしたい件 | taichino.com

Python 2.x 系では、オブジェクトの文字列表現、つまりリストやディクショナリから str 型に変換されるときに非 ASCII 文字が含まれていると、それらがエスケープされてしまうために上記のような表示になってしまいます。どうしても日本語を読める形で表示したいなら、ちょっと面倒な感じですが、以下のようにして目的を実現できます。

$ python2.6
Python 2.6.6 (r266:84292, Mar  3 2011, 19:50:05) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print ", ".join(['あ', 'い', 'う'])
あ, い, う
>>> print "\n".join("%s: %s" % i for i in {'title':'ねじまき鳥', 'author':'村上 春樹'}.items())
author: 村上春樹
title: ねじまき鳥

個々の要素を文字列オブジェクトに変換した上で print 文に渡すことで読める形の日本語を表示しています。

前々から何とかならないかと、悶々とした日々を過ごしていたのですが、先ほど駄目元で以下のコードを試してみたところ無事pretty printに成功しました。一旦json形式にした文字列を、evalでunicode文字列として再評価しています。そうする事でエスケープ表記されたunicode文字からunicodeオブジェクトを作っています。無理矢理感は否めませんが、背に腹は代えられないでしょう!

Pythonで日本語を含んだリストと辞書をpretty printしたい件 | taichino.com

さらに元記事では unicode 文字列が出てきて煩雑さを助長してしまっているのですが、インタープリタの対話モードで日本語を読める形で表示したいという用途だけであれば、unicode 文字列は本質的に関係ありません。まずは次の出力を見てください。

>>> 'あ'
'\xe3\x81\x82'
>>> print 'あ'

str 型の 'あ' をインタープリタ上で入力したときと print 文に渡したときで表示が違います。print 文はデフォルトで sys.stdout に出力するので、'あ' が表示されているのはターミナルの環境変数 (LANG) に設定された文字コードエンコードして表示されているのかなと思います(この辺は怪しいので間違ってたらツッコミください)。

>>> import sys
>>> sys.stdout.write('あ\n')
あ

ここで unicode 文字列を print する場合も考えてみます。

>>> unicode('あ', 'utf-8')
u'\u3042'
>>> print unicode('あ', 'utf-8')
あ

やはり 'あ' が表示されますね。

はまりどころ:printステートメント

先ほどは、日本語を確実に出力するために、
print u1.encode('utf_8')
のようにしましたが、実は、単に
print u1
ように直接unicode型を渡しても、unicode型からstr型へのエンコードが行われます。たいていは正しく日本語を出力できます。

この場合には、先ほどのsys.getdefaultencoding()で得られる文字コードエンコーディング)ではなく、環境変数LANG等のロケールで設定された文字コードを使ってエンコードされます。

このとき使用されるエンコーディングは、sys.stdout.encodingから参照することができます。

PythonのUnicodeEncodeErrorを知る - HDEラボ

によると、unicode 文字列の場合は print 文が実行される過程でエンコードしてから出力してくれるようです。おそらくは内部的に以下のような処理と同じことが行われているのだと思います。

>>> sys.stdout.encoding
'UTF-8'
>>> sys.stdout.write(unicode('あ\n', 'utf-8').encode(sys.stdout.encoding))
あ

前置きが長くなってしまいましたが、標準出力に出力するときに unicode 文字列をそのまま出力することはできないので、結局のところ、環境変数 LANG から得られた文字コード(私の環境では UTF-8)でエンコードすることになります *1 。そのため、(UTF-8エンコードされた) str 型の文字列をそのまま使っても、unicode 文字列にデコードしてから使っても print 文で表示する場合は同じことになります。

Python 2.6 以上では str.format を使用する方がお奨めなので以下のように書くこともできます。

>>> print "{0}, {1}, {2}".format(*['あ', 'い', 'う'])
あ, い, う
>>> print "title: {title}, author: {author}".format(**{'title':'ねじまき鳥', 'author':'村上春樹'})
title: ねじまき鳥, author: 村上春樹

ちなみに Python3 では @ による PEP 3138 のおかげでリストやディクショナリのオブジェクト文字列表現における非 ASCII 文字はエスケープせずに表示してくれます。詳細は Python 3 のオブジェクト文字列表現 - O'Reilly Japan Community BlogString representation in py3k を参照してください。

$ python3.2
Python 3.2 (r32:88445, Mar  5 2011, 00:53:25) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print(['あ', 'い', 'う'])
['あ', 'い', 'う']
>>> print({'title':'ねじまき鳥', 'author':'村上春樹'})
{'author': '村上春樹', 'title': 'ねじまき鳥'}

よく言われる Python3 に移行すれば何とやら、、、ですね(^ ^;;

個人的には割と使用頻度が高いので、prettyprintモジュールにしてpypiに登録しました。インストールはeasy_installから。ソースはgithubから。

Pythonで日本語を含んだリストと辞書をpretty printしたい件 | taichino.com

最後にこれらを踏まえた上で pretty-print を提供する標準ライブラリ pprint を使いましょう。

*1:私の環境は MacOS XLinux ですが Windowsunicode 文字列を直接出力しませんよね?