辞書はPython 3.6以降で注文されますか?


469

辞書は、以前のインカネーションとは異なり、Python 3.6(少なくともCPython実装の下)で注文されます。これは大幅な変更のようですが、ドキュメントの短い段落にすぎません。これは、言語機能ではなくCPython実装の詳細として記述されていますが、将来的には標準になる可能性があることも意味しています。

要素の順序を維持しながら、新しい辞書の実装は以前の辞書の実装よりもどのように優れていますか?

ドキュメントからのテキストは次のとおりです。

dict()PyPyが開発した「コンパクト」表現を使用するようになりました。新しいdict()のメモリ使用量は、Python 3.5と比較して20%から25%小さくなっています。これにより、PEP 468(関数内の** kwargsの順序を維持する)が実装されます。この新しい実装の順序を維持する側面は、実装の詳細と見なされ、信頼すべきではありません(これは将来変更される可能性がありますが、言語仕様を変更する前に、この新しいdict実装をいくつかのリリースで使用することが望ましいです)現在および将来のすべてのPython実装に順序を維持するセマンティクスを義務付けるために、これは、ランダムな反復順序がまだ有効である古いバージョンの言語(Python 3.5など)との下位互換性を維持するのにも役立ちます。(稲田直樹による寄稿問題27350レイモンドヘッティンガーが最初に提案したアイデア。)

2017年12月の更新:Python 3.7では、dict挿入順序の保持が保証されています


2
Python-Devメーリングリストの次のスレッドを参照してください。mail.python.org / pipermail / python -dev / 2016 - September / 146327.html見たことがない場合は、それは基本的にこれらの主題に関する議論です。
mgc 2016年

1
kwargsが順序付けされることになっている場合(これはいい考えです)、kwargsはOrderedDictではなくdictである場合、ドキュメントに別の記述があるにもかかわらず、Pythonキーの将来のバージョンではdictキーが順序付けされたままであると推測できます。
Dmitriy Sintsov 2017年

4
@DmitriySintsovいいえ、そのような仮定をしないでください。これは、PEPの作成中に持ち込まれた問題で、順序保持機能を定義しているため**kwargs、使用される表現は外交的です。**kwargs関数のシグネチャでは、挿入順序保持マッピングであることが保証されています。他の実装がdictを順序付けすることを強制しないようにするため(そして内部的に使用するため)、およびこれが順序付けされていないという事実に依存しないことになっていることを示す方法として、彼らはマッピングという用語を使用しました。OrderedDictdict
Dimitris Fasarakis Hilliard 2017

7
レイモンドヘッティンガーからの良いビデオ解説
アレックス

1
@wazoox、ハッシュマップの順序と複雑さは変更されていません。この変更により、無駄なスペースが減ってハッシュマップが小さくなり、節約されたスペースは(通常は?)補助配列が取るスペースよりも大きくなります。より速く、より小さく、注文した-あなたはすべての3を選ぶことを得る
ジョン・ラRooy

回答:


512

辞書はPython 3.6以降で注文されますか?

これらは挿入順になっています[1]。Python 3.6以降、PythonのCPython実装では、辞書は挿入され項目の順序を記憶していますこれはPython 3.6の実装の詳細と見なされます。Pythonの他の実装全体で保証されOrderedDictいる挿入順序(および他の順序付けされた動作[1])が必要な場合に使用する必要があります。

Python 3.7以降、これは実装の詳細ではなくなり、代わりに言語機能になります。GvRによるpython-devメッセージから

そうしてください。「口述は挿入順序を保つ」が判決です。ありがとう!

これは単にあなたがそれに依存できることを意味します。他のPythonの実装も、Python 3.7の準拠実装にしたい場合は、挿入順の辞書を提供する必要があります。


要素の順序を維持しながら、Python 3.6辞書の実装は以前のものよりどのようにパフォーマンスが向上しますか[2]

基本的に、2つのアレイを保持することにより。

  • 最初の配列はdk_entries、ディクショナリの(タイプPyDictKeyEntry)のエントリを、挿入された順序で保持します。順序を維持することは、新しい項目が常に最後に挿入される追加のみの配列であることによって実現されます(挿入順序)。

  • 2番目のはdk_indicesdk_entries配列のインデックス(つまり、の対応するエントリの位置を示す値dk_entries)を保持します。この配列はハッシュテーブルとして機能します。キーがハッシュされるdk_indicesと、格納されているインデックスの1つになり、対応するエントリがインデックスによってフェッチされますdk_entries。インデックスのみが保持されるため、この配列のタイプはディクショナリの全体的なサイズに依存しますint8_t(タイプ(1バイト)からint32_t/ int64_t4/ 8バイト)32/ / 64ビットビルドの範囲)

以前の実装では、タイプPyDictKeyEntryとサイズのスパース配列をdk_size割り当てる必要がありました。残念なことに、その配列はパフォーマンス上の理由2/3 * dk_sizeいっぱいになることを許可されていなかったため、多くの空のスペースも生じました。(と空きスペースは、まだ持っていたサイズを!)。PyDictKeyEntry

必要なエントリー(挿入されたエントリー)のみが保管され、タイプintX_tXdictサイズに応じて)2/3 * dk_sizeのスパース配列がいっぱいになるため、これは現在のところ当てはまりません。空のスペースがタイプPyDictKeyEntryからに変更されましたintX_t

したがって、明らかに、タイプのスパース配列を作成することは、s PyDictKeyEntryを格納するためのスパース配列よりもはるかに多くのメモリを要求しintます。

興味があれば、この機能に関するPython-Devでの会話全体を見ることができます。


レイモンドヘッティンガーによる最初の提案では、使用されたデータ構造の視覚化を見て、アイデアの要点を捉えることができます。

たとえば、辞書:

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

現在、[キーハッシュ、キー、値]として保存されています:

entries = [['--', '--', '--'],
           [-8522787127447073495, 'barry', 'green'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           [-9092791511155847987, 'timmy', 'red'],
           ['--', '--', '--'],
           [-6480567542315338377, 'guido', 'blue']]

代わりに、データは次のように編成する必要があります。

indices =  [None, 1, None, None, None, 0, None, 2]
entries =  [[-9092791511155847987, 'timmy', 'red'],
            [-8522787127447073495, 'barry', 'green'],
            [-6480567542315338377, 'guido', 'blue']]

視覚的にわかるように、元の提案では、衝突を減らしてルックアップを高速化するために、多くのスペースが基本的に空になっています。新しいアプローチでは、インデックスでスパース性を実際に必要な場所に移動することで、必要なメモリを削減します。


[1]:OrderedDictが存在する場合、「ordered」はdictオブジェクトが提供しない追加の動作を示唆するため、「挿入順序」ではなく「挿入順序」と言います。OrderedDictsは可逆的であり、順序に敏感なメソッドを提供し、主に順序に敏感な等価性テストを提供します(==!=)。dict■現在、これらの動作/メソッドは提供されていません。


[2]:新しいディクショナリの実装は、よりコンパクトに設計されることにより、より優れたメモリのパフォーマンスを発揮します。これが主なメリットです。速度に関しては、その違いはそれほど大きくはありません。新しい辞書がわずかな回帰(たとえば、キールックアップ)をもたらす可能性がある場所もあれば、パフォーマンスのブーストが存在するはずです(反復とサイズ変更が頭に浮かびます)。

導入されたコンパクトさにより、全体的に、特に実際の状況での辞書のパフォーマンスが向上します。


15
では、アイテムが削除されるとどうなりますか?されるentriesリストは、リサイズ?または空白スペースが保持されていますか?それとも時々圧縮されていますか?
njzk2

18
@ njzk2アイテムが削除されると、対応するインデックスはDKIX_DUMMYの値に置き換えられ-2entry配列のエントリはに置き換えられますNULL。挿入が実行されると、新しい値がエントリ配列に追加されます。まだ識別できません。ただし、インデックスが2/3しきい値を超えていっぱいになると、サイズ変更が実行されます。これにより、多くのDUMMYエントリが存在する場合、増加するのではなく縮小する可能性があります。
Dimitris Fasarakis Hilliard

3
@Chris_Randsいいえ、私が見た唯一の実際の回帰は、ビクターによるメッセージのトラッカーにあります。そのマイクロベンチマーク以外に、実際の作業負荷における重大な速度の違いを示す他の問題/メッセージは見たことがありません。新しい辞書がわずかなリグレッション(たとえば、キールックアップ)をもたらす可能性がある場所もあれば、パフォーマンスの向上が見られる場所もあります(反復とサイズ変更が頭に浮かびます)。
Dimitris Fasarakis Hilliard 2017

3
サイズ変更部分の修正:アイテムを削除しても辞書はサイズ変更されず、再挿入すると再計算されます。そのため、dictが作成されd = {i:i for i in range(100)}.popすべてのアイテムが挿入されていない場合、サイズは変更されません。もう一度追加すると d[1] = 1、適切なサイズが計算され、dictのサイズが変更されます。
Dimitris Fasarakis Hilliard 2017

6
@Chris_Rands私はそれが残っていると確信しています。問題は、 ' dict順序付けされている' に関する包括的なステートメントを削除するために私の回答を変更した理由dictで、順序どおりではありませんOrderedDict。注目すべき問題は平等です。dictsには順序に依存しない==OrderedDictsには順序に依存するものがあります。をダンプしOrderedDictdicts順序を区別する比較に変更すると、古いコードで多くの破損が発生する可能性があります。OrderedDicts について変更される可能性がある唯一のものはその実装だと思います。
Dimitris Fasarakis Hilliard 2018

67

以下は、最初の最初の質問への回答です。

Python 3.6 を使用する必要がありますdictOrderedDict

ドキュメンテーションのこの文章は実際にはあなたの質問に答えるのに十分だと思います

この新しい実装の順序を維持する側面は、実装の詳細と見なされ、信頼すべきではありません

dictは、順序付けられたコレクションであることを明示的に意図したものではないため、一貫性を保ち、新しい実装の副作用に頼らない場合は、を使用する必要がありOrderedDictます。

あなたのコードを将来に備えて:)

ここでそれについての議論があります

編集:Python 3.7はこれを機能として保持し ます


1
彼らがそれが本当の機能であるという意味ではなく、実装の詳細である場合、彼らはそれをドキュメントに入れるべきではないようです。
xji 2016年

3
あなたの編集警告についてはわかりません。保証はPython 3.7にのみ適用されるため、Python 3.6のアドバイスは変更されていないと想定しています。つまり、dictsはCPythonで順序付けされていますが、それを
当て

25

更新:Guido van Rossum dict、すべてのPython実装のPython 3.7以降、挿入順序を維持する必要があることをメーリングリストで発表しました


2
キーの順序付けが公式の標準になりましたが、OrderedDictの目的は何ですか?または、今は冗長ですか?
ジョニーワッフル

2
OrderedDictにはmove_to_endメソッドがあり、その等式は順序に依存するため、冗長ではないでしょう:docs.python.org/3/library/…。ジムファサラキスヒリアードの回答に関するメモを参照してください。
fjsj

@JonnyWafflesがジムの回答とこのQ&Aを見るstackoverflow.com/questions/50872498/…–
Chris_Rands

3
コードを2.7と3.6 / 3.7 +で同じように実行する場合は、OrderedDictを使用する必要があります
boatcoder

3
セキュリティ上の理由で
口論を煩わせる

9

上記の議論に追加したかったのですが、コメントする評判がありません。

Python 3.8はまだ完全にはリリースされていませんが、reversed()辞書の機能も含まれています(からの別の違いは削除されていOrderedDictます)。

Dictおよびdictviewは、reversed()を使用して逆の挿入順序で反復可能になりました。(bpo-33462のRémiLapeyreによる寄稿。) python 3.8の新機能を確認する

等値演算子やその他の機能については何も触れられていないOrderedDictため、まったく同じではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.