Contributors Post

Python 3 のオブジェクト文字列表現

|

2008年にリリースされたPython 3。さまざまな機能が追加、更新されている中に私たち日本人にとっては嬉しい機能が追加されています。今回はその機能を提案・設計したご本人に解説をご寄稿いただきました。
また、本記事の内容はPython Hack-a-thon 2010.07でのプレゼンテーションを元にしています

Python 2.x までのオブジェクト文字列表現

Pythonを使ってプログラムを開発していると、デバッグ中などにこんな感じの文字列をよく目にします。

>>> "abc\tdef"
'abc\tdef'
>>> datetime.datetime.now()
datetime.datetime(2010, 7, 9, 13, 37, 49, 107000)

"abc\tdef"datetime.datetime.now()という式を評価した結果が、'abc\tdef'datetime.datetime(2010, 7, 9, 13, 37, 49, 107000)のように表示されています。このような、Pythonの値をデバッグ用に読みやすく整形した文字列を「オブジェクトの文字列表現」と言います。

オブジェクト文字列表現はできるだけ表示するオブジェクトを表すPythonの式と同じになるように生成されており、たとえばリストオブジェクトや辞書オブジェクトは

>>> [1,2,3]
[1, 2, 3]

>>> {1:'abc', 2:'def'}
{1: 'abc', 2: 'def'}

のように、そのままPythonの式として使える形で出力されます。

しかし、これまで日本語文字列を含むデータを出力する時には、あまり使いやすいとは言えませんでした。

>>> "はろー"
'\x82\xcd\x82\xeb\x81['

のように、ASCII文字以外の文字は、すべて'xXX'の形式でエスケープされて出力されてしまっていたからです[1]。せっかくデータが出力されても、これではいちいちPython上で

>>> print '\x82\xcd\x82\xeb\x81['

はろー

としなければ、どのようなデータなのか確認することができませんでした。

文字列データのデバッグであればprintすれば良いのですが、

>>> open("はろー")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

IOError: [Errno 2] No such file or directory: '\x82\xcd\x82\xeb\x81['

のように、エラーメッセージなどに含まれる文字列までエスケープされてしまうため、対処が非常に面倒でした。

Python 3.0からのオブジェクト文字列表現

そこでPython 3.0以降では、文字列中の非ASCII文字もエスケープしないように変更され、日本語文字列を出力すれば、日本語としてそのまま出力できるようになっています。

>>> "はろー"
'はろー'
>>> open("はろー")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

IOError: [Errno 2] No such file or directory: 'はろー'

ただし、タブや改行のような空白文字などは、Python 2と同様にエスケープされます。

>>> "は\t\tー"

'は\t\tー'

これで日本語を扱うプログラムの開発は楽になったのですが、実はまだ問題があります。

Python2では全てのオブジェクト文字列表現はASCII文字しか含まなかったため、通常の環境では出力時にエラーが発生することはありませんでした。しかし、Python3では文字列に使用中のコンソールでサポートされていない文字が含まれていると、エラーが発生するようになってしまったのです。

>>> "\u0550" # ARMENIAN CAPITAL LETTER REH
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

UnicodeEncodeError: 'cp932' codec can't encode character '\u0550' in position 1:
 illegal multibyte sequence

これではデータの中身によってデバッグ時にエラーが発生してしまうため、非常に不便です。このため、Python3ではコンソールでサポートされている文字はそのままで、サポートされていない文字のみ16進形式にエスケープする方法が用意されました。

Pythonを起動する時、環境変数PYTHONIOENCODINGエンコーディング名:backslashreplaceと指定すると、エンコーディング名でサポートされない文字だけが16進形式でエスケープして出力されます。

$ export PYTHONIOENCODING=euc-jp:backslashreplace
$ python
>>> "はろー \u0550" # ARMENIAN CAPITAL LETTER REH
"はろー \u0550"

backslashreplaceはUnicodeエラーハンドラと呼ばれ、Unicode変換エラーが発生した場合の対処方法を指定します。通常のファイルではデフォルトでstrictとなっており、変換エラーがあればそのままUnicodeEncodeError例外を送出します。backslashreplaceは、例外を送出せずに16進形式にエスケープして出力するための指定です。

標準出力ファイルのエンコードとエラーハンドラは環境変数PYTHONIOENCODINGで指定しますが、標準エラー出力のエラーハンドラはbackslashreplace固定となっており、変更することはできません。[2]

自分でファイルをオープンする場合は、

>>> open(filename, "w", errors='backslashreplace')

のように、errors=でエラーハンドラを指定することができます。テキストデータ出力時、どんなデータでもUnicodeEncodeErrorが発生しないようにするのならbackslashreplaceignoreなどのエラーハンドラを指定するようにします。

Python 2.xと同様に、全ての非ASCII文字をエスケープしてしまいたい場合は、組み込み関数ascii()を使用します。

>>> print(ascii("hello, はろー"))

'hello, \u306f\u308d\u30fc'

Python 改善提案書 (Python Enhancement Proposal)

ここで解説したPython3.xでの文字列表現の変更は、実は私が提案し、実現した機能です。たまたまPython開発者メーリングリストでオブジェクトの文字出力についての話題が上がっているのを目にし、そのとき思いついた日本語出力が可能になる実装方法をメールに書き、簡単なパッチを作成したのが始まりでした。

その後、あわよくばそのままPython本体に取り込んでもらえるのではないかと期待していたのですが、Python2との互換性面で影響が大きいためPEPが必要と言われてしまいました。PEPとは 「Python改善提案書」(Python Enhancement Proposal)の略で、Python言語の改訂や標準モジュールの追加など、影響の大きな修正を行う場合には作成することが要求されます。PEPには改善の目的や具体的な実装方法、互換性への影響などを詳しく記述し、開発者メーリングリストで議論を求めます。議論の過程で得られた意見や改善点もPEPに記録し、議論が落ち着いたところでPythonの産みの親であるGuido van Rossum氏の裁定によって導入の可否が決定されます。

Guido van Rossum氏は慈悲深き終身独裁官(Benevolent Dictator For Life:BDFL)としてPython開発者コミュニティに君臨しています。Guidoの裁定は"BDFL裁定"(BDFL pronouncement)と呼ばれ、最終的な判断として尊重されます。議論で合意に達することができなかった場合でも、投票は行われません。開発者たちはメーリングリストで自分の意見を表明しますが、賛否の数に関係なくBDFLの判断によって裁定が下されます。

PEPは2000年頃から導入された制度で、修正の目的や議論をきちんと文書化することによって正しいBDFL裁定を下し、後からなぜこのような修正が加えられたのかを明確に記録することができるようになりました。またPEPが認められなかった場合でも、同じ提案が何度も何度も繰り返し提出されることを防いだり、却下されたPEPを元にもっと優れたPEPを作成して再度チャレンジすることができるようになりました。[3]

けっこう気軽にパッチを投稿したつもりが「PEP書け」と言われてちょっとうろたえてしまった私でしたが、ここまで来ては後に引けません。英語はかなり苦手ですがPEPのドラフト版を作成し、PEP管理者にPEP番号の発行を依頼します。このPEPは「3138」という番号で登録され、Pythonのウエブサイト上で公開されました。[4]

次にこのPEPをPythonの開発者メーリングリストに投稿し、コメントを求めます。ヨーロッパ語圏のユーザにとってはあまりメリットが無く、既存のアプリケーションへの影響も大きな修正だったので反対もありましたが、おおむね賛同を得ることができました。メーリングリストで上がった反対意見や改善点は順次PEPに反映し、パッチもあわせて更新を繰り返します。この間、英語圏の開発者が何度もPEPを添削してくれて、ちゃんと読める英語に直すことができました。議論がある程度進んだところでGuidoの"BDFL裁定"により承認され、Pythonのソースツリーにめでたく登録されることになりました。

英語でのフィードバック

PEPの有無にかかわらず、利用者からPython開発者たちへのフィードバックを送るのはPythonの発展のために非常に重要です。特にアジア圏からのフィードバックはあまり多くないため、Python開発者たちから高い関心を持って読んでもらえることが多いようです。

フィードバックにはある程度の英語力は必要となりますが、技術的なバックボーンがしっかりしていれば私と同程度の英語力―おそらく中学生程度―で十分ではないでしょうか。みんな下手くそなメールを読むのは慣れていますし、相手から揚げ足取りがある訳ではありません。議論の目的はPythonを改善することで、議論の相手もその気持ちは一緒です。利害の対立があるわけではありませんから、下手に議論や文章のテクニックを使ったりする必要もありません。文章がつたなくても、こちらに良い点があれば、相手が積極的に拾い上げてくれます。

長年、色々な英語のメーリングリストを読んできましたが、非英語圏の投稿者の文法や語彙を批判するメールというのは一度も目にした記憶がありません。コミュニケーションですから、できるだけわかりやすい英語を書くように努力する必要はあるでしょうが、完璧な英語を目指すのは無意味です。時制とか定冠詞とか三単現のSだとか、細かいところで間違えるのは仕方ない、とあきらめてしまえば英語を書くのは楽になりますし、文法にこだわるよりもその時間を議論の論理構成やポイントを把握するために使った方が良い結果につながるのではないでしょうか。

昔から言われる話ですが、日本にはかなりの数のオープンソース開発者・利用者がいる割には、バグ報告をしたりパッチを送ったりという活動があまり活発ではありません。Pythonに限らず、いろいろな開発コミュニティにもっと気軽に参加してみましょう。英語でコミュニケーションを取る経験をほんの何回か積むだけで、「あ、なにを怖じ気づいていたんだろう」という気持ちになれると思います。

執筆者紹介:
石本敦夫 @atsuoishimoto
アクシスソフト(株) シニア アーキテクト
日本におけるPythonのメーリングリストやユーザ会を立ち上げた、古株Pythonユーザ。現在もPythonによるメール・情報管理ツールInfoPile(http://www.infopile.jp/)の開発などを活発に行っている。

[1]ここで示している出力例は、WindowsなどシフトJIS環境での一例です
[2]標準出力ファイルのエラーハンドラに指定できるのは、Codec基底クラスのerror文字列引数に与えられる値です。 http://www.python.jp/doc/2.6/library/codecs.html#codec を参照
[3]http://www.python.org/dev/peps/ を参照。またPEP誕生の経緯は『言語設計者たちが考えること』の2章、Guido van Rossum氏へのインタビュー中にも収録されています
[4]PEP3138: String representation in Python 3000

アーカイブ