たまには真面目にPythonのお話をしてみる

どうもはとバスです.最近めちゃくちゃ暑くてかないませんね.

最近,トランプとデートした話や,エアコンが部屋になくてIQがマイナスに振り切れてるようなブログの記事を書いていて,さすがに友人たちに頭を疑われ,父親心療内科の受診と実家での休養を勧められたので,今日は真面目にブログを書きます.

Pythonのリスト内包表記と辞書内包表記

Pythonでよく言われるのが「速度」

速さが足りない!

f:id:flying_hato_bus:20170715003537p:plain

そう,速さが足りません.

Pythonはインタプリンタで一行ずつの処理をしているわけで,圧倒的に遅いという悲しい性を持っています.それに加えてfor文などは,一度リストを作成し,それを参照することでループをさせています.つまり,大きいリストを作れば作るほど,実行には時間がかかります.

それを一発で解決するのが今回お話をするリスト内包表記と辞書内包表記,for文の中で,新たにリストを作成する時,これらを使うとプログラムを短く,かつ速くすることができます.

普通のforループで,偶数だけを要素とするリストを作成する.
hoge=[]

for i in range(5000):
    if i%2 == 0:
        hoge.append(i)

print(hoge)

やっていることは,

最初に0から5000までのリストを作り,その要素の一つを変数 i と置く.

そのiを2で割って,0になるかどうかで判定.

0になった場合,リストに追加する.

ただこれだけ.

Cで書くとこんな感じ.

int i,j=0,huga[5000];

for(i=0;i<5000;i++){
    if(i % 2 == 0) {
        huga[j] = i;
        j++;
    }
}

Pythonでこれを実行すると遅くなる理由が二つある.

  1. 条件を満たす度にリストオブジェクトのappendを参照する

  2. appendをそもそもpythonの関数として呼び出している.

この二つです.この二つがこのループを遅くさせている原因なのですが,参照をループ外で定義してあげればある程度は早くなってくれます.

しかしappendが邪魔をしてしまうので,appendを使わないリスト内包表記のほうが結果的に早くなります.

これをPythonのリスト内包表記を使用して書くとこうなります

hoge = [i for i in range(5000) if i % 2 == 0] 

一行だけで済ますことができます.

説明

Pythonのリスト内包表記の基本構文はこんな感じ,これに色々修飾して書いていくのが多いです.

[変数 for 変数 in iterator]

今回はこのリスト内包表記に後置ifで判別させています. (原則として,Pythonでは後置ifが使用できませんが,リスト内包表記の時だけは使用が可能)

後ろについているif文の条件を満たす時だけ,hogeのリストに値が追加されます.

いちいちリストを作成しなくても,これで要件に応じた処理をできるので,そりゃfor文よりも速くなりますよねって話です.

ちなみに,後ろにくるのはif文で処理できるものなら何でも大丈夫で,例として関数の返り値などが使用できます.

辞書内包表記

次に辞書内包表記です. やっていることは,リスト内包表記を辞書にしただけなのですが,これがなかなか使いやすい.

辞書内包表記の基本構文はこんな感じです.

{key : value for 変数 in iterator}

key,valueともに変数を用いた値を代入することができます.

たとえば,100までの値をkeyとvalueにした辞書を作りたいときは

nya = {str(i) : i for i in range(100)}

こんな感じでできます,ちなみにkeyはstr型ではないとエラーが出るので,整数などをkeyにしようとする際には注意が必要です.

は?これじゃリスト内包表記とほぼ同じじゃん,何言ってんのw

こう思う方もいるかもしれません.

しかし,辞書内包表記は,イテレータの部分を辞書にすることで真価を発揮することができるようになります.

たとえば,辞書のkeyとvalueを関数に飛ばし,帰って来た値を新たに辞書のvalueの部分に代入することができます.

def nyan(noc,n):
    s = "ねこが{}匹 ".format(noc)
    nyaa = "にゃあ"
    neko = s+nyaa*n

    return neko

nyandict={'1':2, '2':4, '3':6}

neko_nyan = { k : nyan(k,v) for k,v in nyandict.items()}

print(neko_nyan)

=> {'1': 'ねこが1匹 にゃあにゃあ', '2': 'ねこが2匹 にゃあにゃあにゃあにゃあ', '3': 'ねこが3匹 にゃあにゃあにゃあにゃあにゃあにゃあ'}
 

こんな感じで,key,valueを引数とした関数を要素にもできます.

forループで書いていってもいいのですが,それだと冗長,そして時間がかかるので,個人的には可読性が少し下がりますが, 辞書内包表記を使用したほうがいいかなと思います.

上のにゃあをforループを使って書いた場合.

for k,v in nyandict.items():
    neko_nyan2[k]=nyan(k,v)

print(neko_nyan2)

forループの方がよくね?って思った方,僕も同感です.

それでは実家に帰って来たので,父親に連れられ心療内科からの隔離病棟で優勝して来ます.ありがとうございました.