web-dev-qa-db-ja.com

OrderedDictを使用しない理由はありますか?

順序付けられた辞書であるcollectionsモジュールの OrderedDict を参照しています。

注文可能という機能が追加されている場合、多くの場合必要がないかもしれませんが、それでも不利な点はありますか?遅いですか?機能が不足していますか?不足しているメソッドはありませんでした。

要するに、なぜすべきではないのですか通常の辞書の代わりにこれを常に使用しますか?

62

OrderedDictdictのサブクラスであり、キーが追加される順序を追跡するためにより多くのメモリが必要です。これは簡単なことではありません。実装により、2番目のdictが背後に追加され、すべてのキーの二重リンクリスト(順序を記憶する部分)と多くのweakrefプロキシが追加されます。 lot遅くはありませんが、プレーンdictを使用する場合に比べてメモリが少なくとも2倍になります。

しかし、それが適切であれば、それを使用してください!それがそこにある理由です:-)

使い方

基本辞書は、キーを値にマッピングする通常の辞書です-「順序付け」されていません。 _<key, value>_ペアが追加されると、keyがリストに追加されます。リストは順序を記憶する部分です。

しかし、これがPythonリストの場合、削除キーの場合、O(n)時間が2回かかります:O(n)時間リスト内のキー、およびリストからキーを削除するO(n)時間。

したがって、代わりに二重にリンクされたリストです。これにより、キー定数(O(1))が削除されます。しかし、キーに属する二重にリンクされたリストノードを見つける必要があります。その操作をO(1)時間にするために、2番目の-非表示-dictは、二重リンクリスト内のノードにキーをマップします。

したがって、新しい_<key, value>_ペアを追加するには、ペアを基本辞書に追加し、キーを保持する新しい二重リンクリストノードを作成し、その新しいノードを二重リンクリストに追加し、キーをその新しいリンクにマッピングする必要があります隠された辞書のノード。作業時間は2倍以上ですが、全体でO(1)(予想されるケース)の時間です。

同様に、存在するキーを削除すると、2倍以上の作業になりますが、全体の予想時間はO(1)です。非表示のdictを使用して、キーの二重リンクリストノードを見つけ、リストからそのノードを削除します。両方の辞書からキーを削除します。

等、それは非常に効率的です。

126
Tim Peters

マルチスレッド

特に同期ポイントとして、ロックなしで複数のスレッドからディクショナリにアクセスする場合。

バニラのdict操作はアトミックであり、Pythonで拡張された型はアトミックではありません。

実際、私はOrderedDictがスレッドセーフ(ロックなし)であるとさえ確信していませんが、非常に注意深くコーディングされ、再入可能性の定義を満たす可能性を無視することはできません。

小悪魔

これらの辞書を大量に作成した場合のメモリ使用量

すべてのコードがこれらの辞書を変更する場合のCPUの使用

7
Dima Tisnek

通常の辞書の代わりにこれを常に使用するべきではないのはなぜですか

Python 2.7、normal OrderedDictを使用すると、参照サイクルが作成されます。したがって、OrderedDictでは、メモリを解放するためにガベージコレクターを有効にする必要があります。はい、cPythonではガベージコレクターがデフォルトでオンになっていますが、無効にしています その用途があります

例えばcPython 2.7.14

_from __future__ import print_function

import collections
import gc

if __== '__main__':
    d = collections.OrderedDict([('key', 'val')])
    gc.collect()
    del d
    gc.set_debug(gc.DEBUG_LEAK)
    gc.collect()
    for i, obj in enumerate(gc.garbage):
        print(i, obj)
_

出力

_gc: collectable <list 00000000033E7908>
gc: collectable <list 000000000331EC88>
0 [[[...], [...], 'key'], [[...], [...], 'key'], None]
1 [[[...], [...], None], [[...], [...], None], 'key']
_

空のOrderedDictd = collections.OrderedDict())を作成して何も追加しなかった場合、またはclearメソッドを呼び出して明示的にクリーンアップしようとした場合でも( _del d_)の前のd.clear()でも、自己参照リストが1つ取得されます。

_gc: collectable <list 0000000003ABBA08>
0 [[...], [...], None]
_

this commit が___del___メソッドを削除して、OrderedDictが収集不能なサイクルを引き起こす可能性を防ぐために、これは事実であると思われます。そのコミットの変更ログに記載されているように:

問題#9825 :collections.OrderedDictの定義から__del__を削除しました。これにより、ユーザーが作成した自己参照の順序付き辞書が永久に収集できないGCガベージになるのを防ぎます。欠点は、__ del__を削除すると、refcntがゼロになったときにすぐにメモリを解放するのではなく、内部の二重リンクリストがGCの収集を待つ必要があることです。


Python 3では、同じ問題の fix は異なる方法で作成され、weakrefプロキシを使用して循環を回避していることに注意してください。

問題#9825:collections.OrderedDictの定義で__del__を使用すると、ユーザーが自己参照型の順序付き辞書を作成できるようになり、永続的に収集できないGCガベージになった。参照サイクルが最初に作成されないように、weakrefプロキシを使用するPy3.1アプローチを復活させました。

3
Day

Python 3.7なので、すべての辞書が順序付けられることが保証されています。Python寄稿者は、dictを順序付けすることに切り替えると負のPython> = 3.7でOrderedDictのパフォーマンスがdictとどのように比較されるかはわかりませんが、それらは同等であるため、どちらも注文されています。

以下も参照してください。

1
Flimm