読者です 読者をやめる 読者になる 読者になる

ローグウェーブソフトウェアのブログ

開発をシンプルに 安全で高品質のコードを 素早くお客様のもとへ

Python コーディング tips #2: 文字列操作

記事紹介 プログラミング IMSL

Part 1に引き続きPythonのtipsをご紹介します。

blog.roguewave.jp

Python コーディング tips #2: 文字列操作

Python coding tips #2: string formatting · Customer Portal

この投稿では文字列操作の新しいやり方を取り上げます。もし "Error: Test case 'test_string_formatting' has failed"のような文字列に対して、 test_string_formattingの部分を変数 test_case_nameで変更可能にしたい場合、以下の様ないくつかの選択肢があります。

文字列結合

最も簡単で初心者向けの設定方法は、単純に文字列を結合してしまうことです。

def print_error(test_case_name):
    print "Error: Test case '" + test_case_name + "' has failed"

理解しやすいですが、シングルクオートが少しやっかいですね。このやり方の大きな問題は、

print "The port  number " + port_number + " is in use"

のように変数が文字列である場合しか使えないということです。もし変数port_numberが文字列なら問題ありませんが、整数だった場合、TypeErrorの例外が発生します。この場合変数を文字列に変換する、という回避策があります。

print "The port  number " + str(port_number) + " is in use"

一方、複数の文字列を結合する場合、やはりコードが見づらくなってきます。

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '" + test_case_name + "' has failed with message " + error + ". Please re-run '" + test_case_name + "' or contact " + admin_name + " at: " + admin_email

モジュロ(%)演算子

2番めの選択肢は、Python 2.Xでより一般的なモジュロ演算子を使うことで、C/C++のprint関数とほぼ同じ挙動を示します。

def print_error(test_case_name):
   print "Error: Test case '%s' has failed" % test_case_name

これにより文字列結合に比べてコードがぐっと見やすくなります。また、非文字列型に対するキャストも不要です。

print "The port number %d is in use" % port_number

最初の例では文字列変数に対して%sを使っていますが、この例では整数型の変数を期待して%dを使用しています。このやり方なら複数のフォーマットを1行で記述することができます。

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '%s' has failed with message %s. Please re-run '%s' or contact %s at: %s" % (test_case_name, error, test_case_name, admin_name, admin_email)

文字列結合の場合にくらべて可読性が向上しました。しかし3-5以上の要素があったり、test_case_name のように複数回使用される要素があるとやはりめんどうです。ここで以下のような書式を使えば変数とフォーマットが関連付けられて多少改善されます。とはいえ辞書を作成しなければなりません。

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '%(tc)s' has failed with message %(error)s. Please re-run '%(tc)s' or contact %(admin_name)s at: %(email)s" % {"tc": test_case_name, "error": error, "admin_name": admin_name, "email": admin_email}

formatメソッド

Python 2.6以降では上記の手続きの代わりに組込型のformat()が導入されました。

def print_error(test_case_name):
   print "Error: Test case '{}' has failed".format(test_case_name)

{}ごとにformat()の引数が置換されます。最大のメリットは型を気にする必要がなくなる、ということでしょう。

print "The answer to {} is {} + {} = {}".format("Question 1", 4, 6.578, 4 + 6.578)

以下のように型を指定すれば型もチェックできます。

print "The answer to {:s} is {:d} + {:f} = {:f}".format("Question 1", 4, 6.578, 4 + 6.578)

また、繰り返し登場する変数に対しても、

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '{}' has failed with message {}. Please re-run '{}' or contact {} at: {}".format(test_case_name, error, test_case_name, admin_name, admin_email)

これは代入用の0始まりのインデックスを使って以下の等価なコードで表せます。

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '{0}' has failed with message {1}. Please re-run '{0}' or contact {2} at: {3}".format(test_case_name, error, admin_name, admin_email)

また、代入に際してはキーワードを使うこともできます。

def print_error(test_case_name, error, admin_name, admin_email):
    print "Error: Test case '{tc}' has failed with message {error}. Please re-run '{tc}' or contact {admin_name} at: {email}".format(tc=test_case_name, error=error, admin_name=admin_name, email=admin_email)

この他にも、ここでは書ききれない様々な方法があります。公式ドキュメントをご覧ください。

7.1. string — 一般的な文字列操作 — Python 2.7.x ドキュメント

編集後記

ローグウェーブのPythonに関連する製品として、PyNLがあります。Pythonで数値/統計ライブラリを扱うための手軽で強力な道具です。 blog.roguewave.jp

blog.roguewave.jp

補足

なお、ブログ原文では最後に、

上で挙げたどのやり方でも問題はありませんが、format()を使えば、内部で型を扱えるため最も幸せになれるでしょう。別の例としてunicode文字列を扱う場合、2番めのモジュロフォーマットはエンコードの問題で失敗します。

# coding=utf-8
a = u'text'
b = "日本のフォルダ"
print "> {0} {1}".format(a, b)
print "> %s%s" % (a, b)

とありますが(なぜ「日本のフォルダ」なのかわかりませんが)、これは環境によります。Windows 10で試したところPython2.7ではたしかに失敗しますが、Python3.5ではどちらもうまく表示できました。なお、Python 3.xではprintは文ではなく関数ですので

print( "> {0} {1}".format(a, b))

のように()をつけて実行してください。

ローグウェーブ セールスエンジニア 柄澤(からさわ)