組み込みdictとOrderedDictの比較 (Python 3.8)

この記事は、マイナビ Advent Calendar 2020 19日目の記事となります。

概要

  • Pythonの組み込みdictクラスが、3.7から挿入順序を記憶するようになりました。
  • もともとあったOrderedDictとの違いや、3.7以降でのOrderedDictの使いどころが気になったので、調べてみました。

公式ドキュメントの記述 (Python 3.8)

辞書は挿入順序を保存するようになりました。 キーの更新は順序には影響が無いことに注意してください。 いったん削除されてから再度追加されたキーは末尾に挿入されます。
(中略)
バージョン 3.7 で変更: 辞書の順序が挿入順序であることが保証されるようになりました。この振る舞いは CPython 3.6 の実装詳細でした。
  • collections > OrderedDict
    • 「いまだ残っている dict との差分」から、気になった箇所を抜粋
    • 「効率的に」が実行速度のことなのかコード量のことなのかが、この記述だけだと不明だが、とりあえずこの記事の下の方ではコード量を比較
OrderedDict に対する等価演算は突き合わせ順序もチェックします。
OrderedDict には、 効率的に要素を末尾に置き直す move_to_end() メソッドがあります。

等価演算の比較

  • OrderedDict
from collections import OrderedDict

d1 = OrderedDict({'one': 1, 'two': 2, 'three': 3})
d2 = OrderedDict({'one': 1, 'two': 2, 'three': 3})
d3 = OrderedDict({'one': 1, 'three': 3, 'two': 2})
d4 = OrderedDict({'one': 1, 'two': 2, 'three': 3, 'four': 4})

print(d1 == d2)  # True
print(d1 == d3)  # False (順序もチェックする)
print(d1 == d4)  # False
  • dict
d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'one': 1, 'two': 2, 'three': 3}
d3 = {'one': 1, 'three': 3, 'two': 2}
d4 = {'one': 1, 'two': 2, 'three': 3, 'four': 4}

print(d1 == d2)  # True
print(d1 == d3)  # True (順序はチェックしない)
print(d1 == d4)  # False

move_to_end

末尾に移動

  • OrderedDict
from collections import OrderedDict

d = OrderedDict({'one': 1, 'two': 2, 'three': 3})
d.move_to_end('two')
print(list(d.keys()))  # ['one', 'three', 'two']
  • 同一の処理をdictで行う場合
    • move_to_end相当の処理のために3行分書く必要あり。
d = {'one': 1, 'two': 2, 'three': 3}
tmp = d['two']
del d['two']
d['two'] = tmp
print(list(d.keys()))  # ['one', 'three', 'two']

先頭に移動

  • OrderedDict
from collections import OrderedDict

d = OrderedDict({'one': 1, 'two': 2, 'three': 3})
d.move_to_end('two', last=False)
print(list(d.keys()))  # ['two', 'one', 'three']
  • 同一の処理をdictで行う場合
    • move_to_end相当の処理のために5行分書く必要あり。
d = {'one': 1, 'two': 2, 'three': 3}
d_tmp = {'two': d['two']}
del d['two']
for k, v in d.items():
    d_tmp[k] = v
d = d_tmp
print(list(d.keys()))  # ['two', 'one', 'three']