普通の辞書とOrderedDictは何が違うのか?【Python 3.7】

Python 3.7から、普通の辞書も順番を保存するようになりました。

では、『普通の辞書(Dict、ディクト)』と『オーダードディクト(OrderedDict、順序付き辞書)』は、何が違うのか?

一番の違いは、『辞書どうしを比較するときに、順序を見るか?見ないか?』という違いだと思います。

なので、プログラムに『辞書どうしを比較』するコードがあって、『順序の一致まで期待』しているなら、Python 3.7以降も引き続きオーダードディクトが必要になります。

また、同じ機能が使えるんだけど『普通の辞書のほうが速い操作』とか、『オーダードディクトのほうが速い操作』があるとのことなので(公式マニュアル OrderedDict オブジェクト)、速さを求める場面でも使い分けるメリットが残っています。

スポンサーリンク

以下、『普通の辞書』と『OrderedDict』の違いです。

辞書(Python 3.7)とOrderedDictの違い

Python 公式マニュアルの『OrderedDict』に、普通の辞書との違いが載っていました。

『いまだ残っている dict との差分』として、箇条書きで紹介されています。

collections — コンテナデータ型
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) オブジェクトの等価判定は、順序に影響されず、通常の辞書と同様です。

出典:class collections.OrderedDict([items])

こういうわけで、辞書の中身の順番までチェックしたいときは、Python 3.7以降でも『OrderedDict』が必要になります。

OrderedDictを使い続けるべきか?

もし、オーダードディクトを使ってきた既存のプログラムがあるなら、Python 3.7 以降も OrderedDict を使うべきだと思います。

Python 3.6 のリリースノートからリンクされている PyPy dict implementation の記事『(JANUARY 22, 2015) Faster, more memory efficient and more ordered dictionaries on PyPy』にも、辞書の順番が重要なケースでは、引き続きOrderedDictの使用を勧める旨が書かれていました。

スポンサーリンク

Python 3.7 から順番が保存されるようになったことで、通常の辞書で対応可能なケースがぐっと増えました。

なので、オーダードディクトを使う場面が減るのは確かです。

けれども、『キーの順番』や『アイテムの順番』まで、辞書の中身を深く比較したいケースが出てきたら、そのときは、OrderedDictの出番になります。

あと、『公式マニュアル OrderedDict オブジェクト』にある通り、『オーダードディクトのほうが速い操作』もありますので、速さを求める場面でもOrderedDictの出番があります。

決算分析システムでOrderedDictを使った理由

デバッグを簡単にするためです。XBRLと闘い始めたころは何もかもが手探りで、普通の辞書だと解析結果を見比べるのにとても不便でした。

(当時の Python 3.4.3 はキーの順番が完全にランダムでした)

それでどうにかできないかと、OrderedDictに追加してみたらタグが順番通りに並んだわけです。勘定科目の取りこぼしとか、何か異常があった時に問題の場所が見つけ易くなりました。

それから数年がたって、Python 3.7から辞書の順番が保存されると聞きました。

とてもありがたい進化です。

あれ?そうすると

『これまでの順序付き辞書が不要になるのでは?』

そう思って調べた時の内容が、この記事です。

結論としては、既存のプログラムはこのまま『順序付き辞書』を使うことにして、新しく作るときは『普通の辞書』を使おうってなりました。

普通の辞書とOrderedDictの使い分けの参考になれば幸いです。

タイトルとURLをコピーしました