msawady’s learning memo

ただのJavaエンジニアが、学んだことをログっていくところ

【Python】key と value が連なった List を Dict に変換する

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 の内部的な挙動 (英語)

How does Python's zip function work internally? - Quora