Python の tips を備忘代わりに
- プロジェクトが変わりまして、Pythonを触ることが増えました
- 掲題の件で悩んだので、パフォーマンスの検証も含めて post しておきます
やりたいこと
こういう List を
hoge_list = ['key1', 'value1', 'key2', 'value2', 'key3', 'value3',,,,]
こういう Dict にしたい
hoge_dict = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3',,,,}
これだけ。
調べて出てきたやり方
困ったときは、stackoverflow ですね。 stackoverflow.com
method1
hoge_dict = dict(zip(hoge_list[0::2], hoge_list[1::2]))
綺麗なワンライナーですね。奇数番目の要素を取り出した List と、偶数番目の要素を取り出した List を zip するというやり方です。
method2
hoge_dict = { hoge_list[i]: hoge_list[i+1] for i in range(0, len(hoge_list), 2) }
dict comprehension を使って、値を順々に key: value に入れていくというやり方です。
method3 (Python3 のみ)
i = iter(hoge_list) hoge_dict = dict(zip(i, i))
Python3 からは zip が遅延評価になっているので、このやり方で iterator から順々に要素を取り出して zip することができます。
パフォーマンス検証
検証用コード
1万個の key と value のセットを並べたリストを、それぞれのやり方で1万回ずつ回して、実行時間を取ります。
from datetime import datetime def method1(hoge_list): return dict(zip(hoge_list[0::2], hoge_list[1::2])) def method2(hoge_list): return {hoge_list[i]: hoge_list[i + 1] for i in range(0, len(hoge_list), 2)} def method3(hoge_list): i = iter(hoge_list) return dict(zip(i, i)) if __name__ == '__main__': hoge_list = [] for i in range(0, 10000): hoge_list.append('key{}'.format(i)) hoge_list.append('value{}'.format(i)) start = datetime.now() for i in range(0, 10000): method1(hoge_list) time1 = datetime.now() - start start = datetime.now() for i in range(0, 10000): method2(hoge_list) time2 = datetime.now() - start start = datetime.now() for i in range(0, 10000): method3(hoge_list) time3 = datetime.now() - start print('time1: {}'.format(time1)) print('time2: {}'.format(time2)) print('time3: {}'.format(time3))
結果
time1: 0:00:13.092214 time2: 0:00:24.749644 time3: 0:00:09.524495
method3 < method1 <<< method2 という結果になりました。
- method3 が method1 より速いのは納得です。「1回 key のリストと value のリストを作ってから zip する」のと、「順々に値を取り出してzipする」のでは、後者のほうがメモリコピーの回数が少ないので速くなりそうですね。
- method2 がこんなに遅いのがちょっとビックリでした。調べたところ、Dict Comprehension は内部的に update() を呼んでいるようなので、「1回ずつupdate()を行う」のと、「zipでまとめてからdictに変換する」のでは確かに後者のほうが速そうですね。
結論
method3を使いましょう。
i = iter(hoge_list) hoge_dict = dict(zip(i, i))
参考ページ
- 各種 comprehension の内部的な挙動(英語)
Stupid Python Ideas: How do comprehensions work?
- zip の内部的な挙動 (英語)