Pythonセットが挿入順序を保持しないのはなぜですか?


12

ディクテーションはPython 3.7以降で挿入順序を保持することが保証されていますが、セットはそうではないことを最近発見して驚きました。

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

この違いの根拠は何ですか?Pythonチームがdictの実装を変更するのと同じ効率の改善は、セットにも適用されませんか?

私は順序付きセットの実装へのポインタや、セットの代わりとしてディクテーションを使用する方法を探していません。Pythonチームが組み込みセットでディクショナリーの順序を維持するのと同時になぜ順序を維持しないのか疑問に思っています。


1
これはあなたの質問に答えますか?Pythonには順序セットがありますか?
Mihai Chelaru

1
いいえ、Pythonには順序付けされたセットが組み込まれていないことを理解しています。dictsが順序付けされるようになったので、なぜそれが当てはまるのか疑問に思っています。
Bart Robinson

4
使用パターンは異なるため、さまざまなユースケースに合わせて最適化されています。セットはCPythonのnull値を持つディクショナリーであるというのはよくある誤解ですが、それは完全に正しくありません。実装は異なります。質問が解決しない場合は、詳細な回答を投稿できます。
WIM

1
「使用パターンは異なるため、さまざまなユースケースに最適化されています。」質問への良い答えはこれについて詳しく説明すると思います。問題は、2つの異なるアプローチを対応するユースケースに最適にする方法についてです。
Karl Knechtel

PyPyは、両方に同じ順序付けを使用しないことに注意dictしてset2.7以降。
MisterMiyagi

回答:


10

セットとディクテーションは、さまざまなユースケース用に最適化されています。セットの主な用途は、順序に依存しない高速メンバーシップテストです。辞書の場合、検索のコストが最も重要な操作であり、キーが存在する可能性が高くなります。セットの場合、要素の存在または不在は事前にわからないため、セットの実装は、見つかった場合と見つからなかった場合の両方に対して最適化する必要があります。また、ユニオンやインターセクションなどの一般的なセット演算の一部の最適化では、パフォーマンスを低下させることなくセットの順序を維持することが困難になります。

どちらのデータ構造もハッシュベースですが、セットがnull値を持つディクショナリとして実装されているというのはよくある誤解です。CPython 3.6のコンパクトなdict実装の前でさえ、コードの再利用はほとんどなく、セットとdictの実装はすでにかなり異なっていました。たとえば、dictsはランダムなプローブを使用しますが、setは線形プローブとオープンアドレッシングの組み合わせを使用して、キャッシュの局所性を向上させます。最初の線形プローブ(CPythonではデフォルトで9ステップ)は一連の隣接するキー/ハッシュペアをチェックし、ハッシュ衝突処理のコストを削減することでパフォーマンスを向上させます-連続メモリアクセスは分散プローブよりも安価です。

理論的には、CPythonのセット実装をコンパクトな辞書に類似するように変更することは可能ですが、実際には欠点があり、注目すべきコア開発者はそのような変更を行うことに反対していました。

セットは順不同のままです。(なぜですか?使用パターンは異なります。また、実装も異なります。)

グイドファンロッサム

セットは、挿入順序を保持するのに適していない別のアルゴリズムを使用します。順序が必要な場合、セット間操作は柔軟性と最適化を失います。セット数学は、順序付けられていないセットの観点から定義されています。つまり、セットの注文は当面はありません。

レイモンドヘッティンガー

3.7のセットをコンパクト化するかどうかの詳細な議論、およびそれが決定されなかった理由についての回答は、python-devメーリングリストにあります。

要約すると、主なポイントは、使用パターンが異なる(** kwargsなどの挿入順序付けディクショナリは有用であり、セットの場合はそれほど有用ではない)ことであり、セットを圧縮するためのスペース節約はそれほど重要ではありません(キー、ハッシュ、値とは対照的に高密度化)、および前述のセットでの線形プローブ最適化は、コンパクトな実装と互換性がありません。

最も重要な点をカバーするレイモンドの投稿を以下に再現します。

2016年9月14日午後3時50分に、Eric Snowは次のように書いています。

次に、セットについても同じようにします。

私が誤解していない限り、レイモンドはセットに同様の変更を加えることに反対しました。

そのとおり。ここでは、人々が暴走し始める前に、このテーマについていくつかの考えを示します。

  • コンパクトな辞書の場合、スペースの節約は、インデックスによって消費される追加のスペースと、キー/値/ハッシュ配列の割り当ての改善により、キー/値/ハッシュ配列の密度の向上による相殺よりも大きなメリットとなります。ただし、セットの場合、インデックスと割り当て超過がまだ必要なため、ネットははるかに有利ではありませんでしたが、3つの配列のうち2つだけを高密度化することによってのみスペースコストを相殺できます。言い換えると、キー、値、およびハッシュのためのスペースを無駄にした場合、圧縮はより理にかなっています。これら3つのうちの1つを失うと、説得力がなくなります。

  • セットの使用パターンは、dictsとは異なります。前者はより多くのヒットまたはミスのルックアップを持っています。後者は、欠落したキー検索が少なくなる傾向があります。また、セット間操作の一部の最適化では、パフォーマンスに影響を与えずにセットの順序を維持することが困難になります。

  • セットのパフォーマンスを改善するために代替パスを追求しました。コンパクト化(スペースの獲得がそれほど多くなく、追加の間接化のコストが発生する)の代わりに、線形プローブを追加して、衝突のコストを削減し、キャッシュのパフォーマンスを向上させました。この改善は、私が辞書に対して提唱した圧縮アプローチとは両立しません。

  • 現時点では、辞書への順序付けの副作用は保証されていないため、セットも順序付けされると主張し始めるのは時期尚早です。ドキュメントは既にOrderedSetを作成するためのレシピ(https://code.activestate.com/recipes/576694/)にリンクしてい ますが、取り込みはほぼゼロのようです。また、Eric Snowが高速のOrderedDictを提供してくれたので、MutableSetとOrderedDictからOrderedSetを構築するのはこれまでになく簡単ですが、典​​型的なセット間データ分析は実際にはあまり重要ではないため、本当の関心はありません。注文の必要性または注意。同様に、高速メンバーシップテストの主な用途は、順序にとらわれないことです。

  • そうは言っても、PyPIに代替セット実装を追加する余地はあると思います。特に、注文可能なデータには、キーの範囲全体を比較することでセットからセットへの操作を高速化できる興味深い特殊なケースがいくつかあります(https://code.activestate.com/recipes/230113-implementation-of-を参照) 開始点としてのセット使用ソートリスト)。IIRC、PyPIには、セットのようなブルー​​ムフィルターとカッコウハッシングのコードがすでにあります。

  • 主要なコードブロックをPythonコアに受け入れるのはエキサイティングですが、正当な理由がない限り、他のデータ型のより大規模な書き直しに関与することは許されません。

–レイモンドヘッティンガー

[Pythonの-DEV]パイソン3.6辞書はコンパクトになり、専用バージョンを取得します。およびキーワードは、2016年9月に注文されます。


2

ディスカッション

あなたの質問は密接に関係しており、Python-devsについてはかなり前からすでにかなり議論されています。R.ヘッティンガーは、そのスレッドの理論的根拠のリストを共有しました。この問題の現状は、T。ピーターズからのこの詳細な返答の直後に、自由記述形式で表示されます。

要するに、挿入順序を維持する最新のディクテーションの実装は一意であり、セットでは適切とは見なされません。特に、dictsはPythonを実行するためにどこでも使用さ__dict__れます(オブジェクトの名前空間など)。現代の口述の背後にある主な動機は、サイズを縮小し、全体的にPythonのメモリ効率を高めることでした。対照的に、セットはPythonのコア内のdictsよりも普及していないため、そのようなリファクタリングを思いとどまらせます。現代のdict実装に関するR. Hettingerの講演も参照してください。


展望

Pythonのセットの順序付けされていない性質は、数学的なセットの動作に似ています。順序は保証されません。

対応する数学的概念は順序付けされておらず、順序などを課すのは奇妙です-R.ヘッティンガー

場合、任意の種類の順序を Pythonでセットに導入し、次いで、この現象は完全に別個の数学的構造、すなわち順序付けられたセット(またはOset)に準拠してしまいます。Osetsは、数学、特に組み合わせ論において別の役割を果たします。オセットの実用的なアプリケーションの1つは、ベルの変更に見られます。

順序付けられていないセットがあることは、ほとんどの現代の数学、すなわちSet Theoryを固定解除する非常に一般的でユビキタスなデータ構造と一致しています。私は、Pythonの順序付けられていないセットを用意することをお勧めします。

このトピックを展開する関連記事も参照してください。

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