組み込み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']