こしあん
2019-02-26

PythonのDict/OrderedDictの環境依存について


PythonのDictionaryは順番が保証されません。なので、Dictionaryの順番を意識したいときは「OrderedDict」を使うというのが教科書的な解決方法でした。しかし、Python3.7(言語仕様化されていない状態なら3.6)からDictの順番が保証されるようになったとのことです。その環境依存についてメモしておきます。

参考:https://stackoverflow.com/questions/1867861/dictionaries-how-to-keep-keys-values-in-same-order-as-declared

OrderedDictの場合

OrderedDictを使えばPythonのバージョンに関係なく順番が保証されます。

from collections import OrderedDict

dict = OrderedDict([["a3",0],["a2",1],["a1",2]])
dict["a3"]=-1
print(dict)
#OrderedDict([('a3', -1), ('a2', 1), ('a1', 2)])

文字通りちゃんと順番が保証されています。

通常のDictの場合

次はちょっと怪しいケースです。Python3.6.4での実行結果で通常のDictを実行してみます。

dict = {"a3":0,"a2":1,"a1":2}
dict["a3"]=-1
print(dict)
#{'a3': -1, 'a2': 1, 'a1': 2}

一見順番が保証されているように見えます。OrderedDictを使っていないのに順番が保証されるのはなぜでしょう。実はこの挙動が環境依存なのです。

StackOverFlowを読んでたら目が点でした。

From Python 3.6 onwards, the standard dict type maintains insertion order by default.
(Python3.6からは、通常のDictもデフォルトで代入の順番が維持されるようになった)

ただしPython3.6以前では通常のDictの順番が保証はされていないので、互換性の副作用を避けるためにはOrderedDictを使ったほうが良いとのこと。あくまで通常のDictの順番維持は環境依存であるということを気にしたほうが良さそうです。

より正確にいうと(別のコメントでも言及されていますが)、Dictのキーの順番維持が言語仕様化されたのはPython3.7からなので、3.7以降だけに限定するならOrderedDictを使わなくてもよくなります。

詳細な記事

調べたらもっと詳しい記事がありました。Dictのハッシュの部分から分析していますね。

Python3.6のdictキー順維持と、hash randomizeによるDoS回避の関係について
http://www.freia.jp/taka/blog/python3-hash-randomie/index.html

この記事からの引用です。

  1. object._hash_ のhash collisionによるDoS攻撃を回避するために、Python3.3で起動毎にhashをランダム化した
  2. これによって、hashテーブルの順番で並んでいた特定のdictキー列も、起動毎にランダム化された(副作用)
  3. Python3.6の CPython実装 で、dictキーを挿入順で維持するキー列をhashテーブルtとは別に持つようになったため、キー順が object._hash_ の結果に依存しなくなった(これは1のDoS回避と反しない)
  4. Pythonの言語仕様は変わっていないので、dictキーを挿入順で維持するかどうかはPython実装に依存している

はい?? んやねんそれ。以前のPythonのDictってテーブル順なってなかったの?ほげぇ…

結論

自分の中では以下のような理解になりました。

  • Python3.7以降ならOrderedDictを使わなくても言語仕様上Dictの順番が維持される
  • Python3.6ではだいたい順番は維持されるけど、言語仕様ではないので、ごく稀な特殊な状況で順番が維持されなくてもおかしくはない
  • Python3.6以前は順番が維持されないので、普通にOrderedDictを使え

ざっとの理解なので違うかも知れません。とても勉強になりました。

Related Posts

Kerasでカスタム損失関数を用いたモデルをロードするとValueError... Kerasで損失関数を独自に定義したモデルを保存した場合、load_modelで読み込むと「ValueError: Unknown loss function」とエラーになることがあります。その解決法を示します。 参考:https://github.com/keras-team/keras/is...
Google Colaboratoryで保存したKerasのモデルを読み込むとValueError... Google Colaboratory(Colab)上のKerasでh5形式で保存したモデルをダウンロードして、load_modelすると「TypeError: ('Keyword argument not understood:', 'data_format')」とエラーが発生して読み込めないこ...

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です