Python 3.7から、普通の辞書も順番を保存するようになりました。
では、『普通の辞書(Dict、ディクト)』と『オーダードディクト(OrderedDict、順序付き辞書)』は、何が違うのか?
一番の違いは、『辞書どうしを比較するときに、順序を見るか?見ないか?』という違いだと思います。
なので、プログラムに『辞書どうしを比較』するコードがあって、『順序の一致まで期待』しているなら、Python 3.7以降も引き続きオーダードディクトが必要になります。
また、同じ機能が使えるんだけど『普通の辞書のほうが速い操作』とか、『オーダードディクトのほうが速い操作』があるとのことなので、速さを求める場面でも使い分けるメリットが残っていました。
以下、『普通の辞書』と『OrderedDict』の違いです。
辞書(Python 3.7)とOrderedDictの違い
Python マニュアルの『OrderedDict』に、普通の辞書との違いが載っていました。
『いまだ残っている dict との差分』として、箇条書きで紹介されています。
collections — コンテナデータ型
(Python) OrderedDict オブジェクト
この中で、普通の辞書に無いのが、『比較するときに順序の一致まで見る』ところですね。
OrderedDict に対する等価演算は突き合わせ順序もチェックします。
(The equality operation for OrderedDict checks for matching order.)
辞書とOrderedDictを比較するコード例
普通の辞書 (Dict) と順序付き辞書 (OrderedDict) を比較するコードを書きました。
アイテムの順番だけ変えた辞書を作って、辞書同士を比較しています。
オーダードディクトとして作った辞書では、中身の順番が違うので『False』を返すところがポイントです。
"""『普通の辞書』と『OrderedDict』を作って比較する"""
# 辞書にするアイテムリストを作る
a_items = [(1, 'a'), (2, 'b')]
b_items = [(2, 'b'), (1, 'a')] # 順番だけ変更
print('普通の辞書どうしは、順序を見ない。')
a = dict(a_items)
b = dict(b_items)
print('a: %s' % a)
print('b: %s' % b)
print('a == b: %s' % (a == b))
print('\nオーダードディクトどうしは、順序の一致も見る。')
from collections import OrderedDict
od_a = OrderedDict(a_items)
od_b = OrderedDict(b_items)
print(od_a)
print(od_b)
print('od_a == od_b: %s' % (od_a == od_b))
print('\n『普通の辞書』と『OrderedDict』では、順序を見ない。')
print('a == od_a: %s' % (a == od_a))
print('a == od_b: %s' % (a == od_b))
print('b == od_a: %s' % (b == od_a))
print('b == od_b: %s' % (b == od_b))
Python 3.7.3 での実行結果です。
オーダードディクト同士では、中身の順序が違うので『False』を返しています。
普通の辞書どうしは、順序を見ない。
a: {1: 'a', 2: 'b'}
b: {2: 'b', 1: 'a'}
a == b: True
オーダードディクトどうしは、順序の一致も見る。
OrderedDict([(1, 'a'), (2, 'b')])
OrderedDict([(2, 'b'), (1, 'a')])
od_a == od_b: False
『普通の辞書』と『OrderedDict』では、順序を見ない。
a == od_a: True
a == od_b: True
b == od_a: True
b == od_b: True
これはたしかに、Python マニュアルにある通りの動作になっています。
OrderedDict 間の等価判定は順序が影響し、 list(od1.items())==list(od2.items()) のように実装されます。
OrderedDict オブジェクトと他のマッピング (Mapping) オブジェクトの等価判定は、順序に影響されず、通常の辞書と同様です。
こういうわけで、辞書の中身の順番までチェックしたいときは、Python 3.7以降でも『OrderedDict』が必要になります。
OrderedDictを使い続けるべきか?
もし、オーダードディクトを使ってきた既存のプログラムがあるなら、Python 3.7 以降も OrderedDict を使うべきだと思います。
Python 3.6 のリリースノートからリンクされている PyPy dict implementation の記事『(morepypy.blogspot.com) (JANUARY 22, 2015) Faster, more memory efficient and more ordered dictionaries on PyPy』にも、辞書の順番が重要なケースでは、引き続きOrderedDictの使用を勧める旨が書かれていました。
Python 3.7 から順番が保存されるようになったことで、通常の辞書で対応可能なケースがぐっと増えました。
なので、オーダードディクトを使う場面が減るのは確かです。
けれども、『キーの順番』や『アイテムの順番』まで、辞書の中身を深く比較したいケースが出てきたら、そのときは、OrderedDictの出番になります。
あと、『(Python) OrderedDict オブジェクト』にある通り、『オーダードディクトのほうが速い操作』もありましたので、速さを求める場面でもOrderedDictの出番があります。
決算分析システムでOrderedDictを使った理由
デバッグを簡単にするためです。XBRLと闘い始めたころは何もかもが手探りで、普通の辞書だと解析結果を見比べるのにとても不便でした。
(当時の Python 3.4.3 はキーの順番が完全にランダムでした)
それでどうにかできないかと、OrderedDictに追加してみたらタグが順番通りに並んだわけです。勘定科目の取りこぼしとか、何か異常があった時に問題の場所が見つけ易くなりました。
それから数年がたって、Python 3.7から辞書の順番が保存されると聞きました。
とてもありがたい進化です。
あれ?そうすると
『これまでの順序付き辞書が不要になるのでは?』
そう思って調べた時の内容が、この記事です。
結論としては、既存のプログラムはこのまま『順序付き辞書』を使うことにして、新しく作るときは『普通の辞書』を使おうってなりました。
普通の辞書とOrderedDictの使い分けの参考になれば幸いです。