Python Tips Vol.1

べ……別に、ニシキヘビのお菓子じゃないんだからねっ!
………………
まあ、そもそもスペルが違いますが(正しくは、chips)。

ちゃっかりVol.1なんて書いてますが、果たして今回の記事すらまともに書けるか不安なところでもあります。
てゆうか、今までの記事がまともだったかという疑問が飽くなき探求心を揺さぶります。しかし、まあ、ここではそれは無視しましょう。
知らない方がいいことも、世の中にはたくさんあるのです。

それはさておき。

とりあえず、Tipsには細々とした、何を今更という内容のメモを書いて置こうと思います。記念すべき第1回、序話であり最終話でもあるかもしれない今日のトピックスは以下になります。

  • タプルを辞書型のキーとして使う
  • エスケープシーケンスを使わない文字列
  • enumerate()で繰り返し処理
  • リスト内包表記のすすめ

項目を見て気づかれた方もいらっしゃると思いますが、これは僕が参考書片手に「へえ、こんなのがあるんだー」とか言って、サンプルをいじったもののメモです。流石に、参考書に載ってるものそのままをダイレクトに、鮮度第一でお送りすることはしませんが、とは言え五十歩百歩な中身だと思って下さい。

タプルを辞書型のキーとして使う:

タプル自体が何なのかって人のために。
タプルというのは、不憫な子です。
省略した文字を追加すると。
タプルというのは、(僕のようにその使い道がよく分からない人を)不憫(な気持ちにさせるよう)な子です。

リストのように、あるいは辞書のように要素がいくつも並んで入ってるんですが、この子は変更ができません。つまり、書き換え不可です。
ただし、タプル同士を足し合わせることで、新たなタプルを生成することはできます。

#!/usr/bin/env python
#coding: utf-8
a=(1,2,3)
b=(4,5,6)
c=a+b
print c

なんてすると、

(1, 2, 3, 4, 5, 6)

みたいになります。

で、辞書型のキーにタプルが使えることの利点は、複数項目を組み合わせてキーを作ることができるということです。
どういうことかっていうと、たとえば今a、b、cという3つの地点を通って目的地まで行くとしましょう。ルート毎にかかった時間を、通った順番をキーとして辞書型に登録するとしたら、

#!/usr/bin/env python
#coding: utf-8

time={("a", "b", "c"):5, ("a", "c", "b"):4, ("c", "a", "b"):6}
print time[("a", "b", "c")], " hour"

みたいにできて、実行すると、

5  hour

と表示されます。まあ、あまり良い例ではないですが、こんな風に使えるというだけです。ちなみに、物の本には、ドメインIPアドレスをキーとして登録するという例が載ってました。
次のはただの遊びです。吐き気をもよおされた方はすかさずスクロールしてください。

#!/usr/bin/env python
#coding: utf-8

test={(u"天然長男",u"ツンデレ次男"):"うろたえるの好きだな",
      (u"ツンデレ次男",u"天然長男"):u"あなたとは違うんです",
      (u"天然長男",u"ツッコミ令嬢"):u"カレー(甘口)がいいな",
      (u"ツッコミ令嬢",u"天然長男"):u"純粋さが時に腹立つ",
      (u"天然長男",u"ツインテール雑技団員"):u"守ってみせる",
      (u"ツインテール雑技団員",u"百面相王子"):u"友達なのに……",
      (u"雪国ダンディズム",u"ツインテール雑技団員"):u"お前のそれは飛ぶためにある",
      (u"天才的風呂嫌い",u"ツインテール雑技団員"):u"仲良くなりたいよー",
      (u"天才的風呂嫌い",u"ツッコミ令嬢"):u"2,3日入らなくても死なないって",
      (u"ツッコミ令嬢",u"天才的風呂嫌い"):u"死にます"}

print test[(u"ツッコミ令嬢",u"天然長男")]

元ネタは某テイルズディストリビューションおもしろくしたかったんですが案の定失敗しました。
ちなみに、これはキーにおける前のキャラから見た後のキャラへの関係性みたいな構成になっています。組み合わせに対する付加的な情報をまとめるのに便利そうだと思ったので、こんな例を載せました。
いや、載せなくて良かったかもしれませんが、それはそれとして流しましょう。

エスケープシーケンスを使わない文字列:

何言ってんだこいつと思った方がいるかもしれませんが、僕も何言ってんだ自分と思ってるんでお気になさらず。
とりあえず、例からいきます。

#!/usr/bin/env python
#coding: utf-8

print u"これを表示したい:",u"\\begin{document}から\\end{document}までに本文を書きます."
print u"\\begin{document}から\\end{document}までに本文を書きます."  #1
print u"\begin{document}から\end{document}までに本文を書きます."    #2
print ur"\begin{document}から\end{document}までに本文を書きます."   #3
print ur"\\begin{document}から\\end{document}までに本文を書きます." #4

これを実行したらどうなるかって、

これを表示したい: \begin{document}から\end{document}までに本文を書きます.
\begin{document}から\end{document}までに本文を書きます.                                           egin{document}から\end{document}までに本文を書きます.
\begin{document}から\end{document}までに本文を書きます.
\\begin{document}から\\end{document}までに本文を書きます.

こうなります。
違いがお分かりでしょうか?環境によって変わるでしょうが、2段目は先頭の部分が欠けて、1段目の方に干渉してしまいます。\b(文字列中ではbackspace)が働いてしまうからです。液晶が小さすぎなければ、ブラウザを最大表示することで、どんな問題が起きているかお分かりになっていただけるかもしれません。
これを解決する方法は2通りあります。
まずは、#1のような方法。次に#3のような方法です。#1はともかくとして、#3で注目したいのは文字列の前に付加されたrです。このrを付加した文字列をraw文字列と言うんだそうです。

そうですねえ、たとえたら\TeXでいうverbatimみたいなもんですかね。
わかりにくいですか?そう仰られても、この日記が一度でもわかりやすかったことがありましたか?としか言いようがございません。

ファイルパスなんかを書くときに便利そうだなあ、なんて思いました。

enumerate()で繰り返し処理:

シェルスクリプトなんかでfor文使ってる人は、Pythonのfor文に親しみが持てるのではないでしょうか。
Pythonにおけるfor文の構文については割愛するとして。
たとえば、以下のコード。

#!/usr/bin/env python
#coding: utf-8
name=["ImagineBreaker","Railgun","Accelerator","Teleport"]
cnt=0
dic={}
for skill in name:
    dic[cnt]=skill
    cnt+=1

print dic

この実行結果は、

{0: 'ImagineBreaker', 1: 'Railgun', 2: 'Accelerator', 3: 'Teleport'}

となるんですが、このようにリストの内容にインデックスをつけて、辞書を作りたいとか思うことがあったとしましょう。
これ、cntを外に準備するの面倒ですよね。
というわけで、enumerate()の登場です。

#!/usr/bin/env python
#coding: utf-8
name=["ImagineBreaker","Railgun","Accelerator","Teleport"]
dic={}
for cnt,skill in enumerate(name):
    dic[cnt]=skill

print dic

実行結果は先ほどと変わりません。と言うより、変わったら意味ないですね。
どうでしょう、cntに関わる部分が無くなって、ちょっとすっきりしてませんか?
すごいですよね、enumerate()。ちょっとした感動です。

ところで、元ネタが分かる人は途中こいつ何やってんだと思ったかもしれません。すみませんが、これに関しては狙った訳ではなく、素でそうなったんです。なんせ、書いてる途中に自分で何やってんだと思ってしまったんですもの。
まあ、人生そんなもんです。

リスト内包表記のすすめ:

人の上には人がいるから、上で生きたきゃ勉強しなさいと言いたかったのにね。
ともかく。
便利な便利なリスト内包表記についてです。上のenumerateと合わせてご覧いただきたいのですが、どうしてかと言うと、上の例と同じことをリスト内包表記で簡潔にできることを示したいからです。

とりあえず、上を見ろというのも不親切なので、同じコードから。

#!/usr/bin/env python
#coding: utf-8
name=["ImagineBreaker","Railgun","Accelerator","Teleport"]
dic={}
for cnt,skill in enumerate(name):
    dic[cnt]=skill

print dic
# 実行結果
# {0: 'ImagineBreaker', 1: 'Railgun', 2: 'Accelerator', 3: 'Teleport'}

こんなコードだったんですが、これだとまだ外でdicを宣言しています。
邪魔です。

そんな訳で、リスト内包表記を使うパターンは、

#!/usr/bin/env python
#coding: utf-8
name=["ImagineBreaker","Railgun","Accelerator","Teleport"]
# list comprehension
name=[[cnt,skill] for cnt,skill in enumerate(name)]
print name
dic=dict(name)
print dic

で、これを実行すると、

[[0, 'ImagineBreaker'], [1, 'Railgun'], [2, 'Accelerator'], [3, 'Teleport']]
{0: 'ImagineBreaker', 1: 'Railgun', 2: 'Accelerator', 3: 'Teleport'}

です。これは、

  1. リストnameを、キーとなる数値と元の要素のリストのリストに書き換える。
  2. シーケンスから辞書を作るdictで辞書を作る。

ということをしています。
先の例では、辞書に要素を追加する段階でfor文を使っていましたが、ここではその事前準備の部分でfor文を使っています。リスト内包表記は、この事前準備の部分を担っていますね。

もっと短くするなら、

#!/usr/bin/env python
#coding: utf-8
name=["ImagineBreaker","Railgun","Accelerator","Teleport"]
dic=dict([[cnt,skill] for cnt,skill in enumerate(name)])
print dic

とでもしますか。実行結果は、当然ながら、

{0: 'ImagineBreaker', 1: 'Railgun', 2: 'Accelerator', 3: 'Teleport'}

です。

まとめ:

リスト内包表記は、便利だけど凝った使い方すると読みづらそう……