なぜPython辞書はpython3.7で元に戻せないのですか?


11

3.7以降、標準のPython辞書は挿入順序を維持することが保証されています。(*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

つまり、dictキーは厳密な順序で保持されます。原則として、これにより鍵を元に戻すことができます。ただし、次のいずれも機能しません。

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

質問:この動作の背後にある理由は何ですか?dictsが元に戻せないのはなぜですか?今後、この動作を変更する予定はありますか?

もちろん回避策は機能します:

for k in reversed(list(d.keys())): 
    print(k)

(*)実際のところ、この投稿で説明されているように、これはすでにpython 3.6の一般的なインストールに当てはまります。


更新Python 3.8から、 dictsは実際には元に戻せます。受け入れられた答えは、この決定につながったGuidoと他のコア開発者との間の議論に言及しています。一言で言えば、彼らは実装の努力とユーザーにとっての実際の利益に対して言語の一貫性に重みを付けました。

回答:


5

ドキュメントから:

反転seq

逆を返しますiterator。seqは、__reversed__()メソッドを持っているか、シーケンスプロトコル(0から始まる整数引数を持つ__len__()メソッドと__getitem__()メソッド)をサポートするオブジェクトでなければなりません。

dictオブジェクトが実装されていません__reversed__。後者の方法の両方を実装します。ただし、__getitem__(0から始まる)整数ではなく、キーを引数として受け取ります。

理由については、これはすでに提案され、ここで説明されています

編集:

これらの引用は、Python-Devメーリングリストからのものです(スレッド「Add __reversed__ methods for dict」、25。05. 18に開始)。「概念的な」引数から始めます。最初の引数は、Antoine Pitrouによるものです。

OrderedDictがすでにreverse()をサポートしていることには何の価値もありません。議論は両方の方向に進む可能性があります:

  1. dictは最近のOrderedDictに似ているので、reversed()もサポートするはずです。

  2. OrderedDictを使用して、順序付けに関心があることを明示的に通知できます。辞書に何かを追加する必要はありません。

私の考えでは、通常のディクテーションの挿入順序は保証されていないため、概念が定着し、ディクテーションに関する日常の考え方の一部となるにはしばらく時間がかかります。それが発生すると、ユースケースが出現し、ある時点で__reversed__が追加されることはおそらく避けられません。実装は簡単なようであり、順序付けられた有限のコレクションが可逆的であると期待することは、概念的な飛躍ではありません。

レイモンド・ヘッティンガーの返信が続く:

辞書が挿入順序を追跡するようになったことを考えると、最新の挿入(つまり、タスク辞書で最後に追加されたタスクのループ)を知りたいのは理にかなっているようです。その他の考えられる使用例は、Unixのtailコマンドの使用方法に対応している可能性があります。

これらのユースケースが発生した場合、__ reversed__が既にサポートされていることで、popitem()呼び出しとそれに続く再挿入を使用した醜い回避策を実装したくない場合があります。

メーリングリストで表明された主な懸念、これが少なくとも一部の実装で過度の膨張またはメモリ効率の低下(単一リンクリストではなく二重リンクリストを持たなければならない)であるということでした。):

「注文する」は「リバーシブル」という意味ではありません。たとえば、単一のリンクリストは順序付けされていますが、元に戻すことはできません。

CPython実装は効率的なを提供できますが__reverse__、追加__reverse__すると、すべての Python実装がそれを提供することが期待されます。たとえば、一部のPython実装では、ハッシュマップ+単一のリンクリストを使用してdictを実装できる場合があります。__reverse__が追加された場合、それはもう不可能です。

メーリングリストに戻り、最後の2つのメッセージを示します(どちらも08.06.2018に投稿されています)。最初はMichael Selikからです。

コンセンサスはv3.8に含めるために+1であると私は言っていますか?

スレッドの最後のポイントは、稲田直樹がさまざまな実装を調査し、この機能を3.8に含めてもよいと判断したことです。私が理解しているように、Guidoは、MicroPythonによるv3.7の実装を待つというINADAのアドバイスに同意しました。INADAは気が変わったので、それがすべて賛成だと思いますか?

グイドファンロッサムのメッセージで締めくくる:

それは私には正しいように思えます。次に、これが当てはまる2つのバージョンが作成されます。

  • 3.6 CPythonで言語仕様で順序保持が実装された場所

  • 3.7言語仕様にも追加されました

他の回答とコメントに記載されているように、reversed()バージョン3.8(14.10.2018)以降、dictsとdictviewsの両方でサポートされています。


5
2番目の引用は、そのスレッドからのバイアス選択のようです。コンセンサスは、機能が3.8で追加されることです。また、Python 3.7より前のdictreversed
バージョン

私は戦いで犬を飼っていません。彼の最初の反応を引用しました。しかし、あなたは正しい、よく指摘されたポイント-私は引用を削除しました。
gst

1
ありがとう。python-devのディスカッションスレッドが明らかになりました。実際、この機能は2日前にリリースされたpython 3.8にすでに実装されています(2019年10月14日)。
ノルマニウス


ドキュメントの引用は役に立ちません、それは次の質問を頼むだけです "それで、なぜdictタイプは実装しないの__reversed__ですか?" python-devリンクには有用なコンテンツがありますが、関連する部分は回答で直接再現する必要があります(このようなオフサイトリンクは腐敗する傾向があるため)。
WIM


1

Python 3.8の更新

Dictおよびdictviewは、reversed()を使用して逆の挿入順序で反復可能になりました

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>

この回答は、他の回答、コメント、または質問自体でカバーされていない新しい何かに貢献していますか?
normanius

@normaniusこれには、lilのビジュアルコードサンプルがあり、クイックブラウザに役立つかもしれません
jamylak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.