web-dev-qa-db-ja.com

OrderedDictの値が等しくないのはなぜですか?

Python 3:

>>> from collections import OrderedDict
>>> d1 = OrderedDict([('foo', 'bar')])
>>> d2 = OrderedDict([('foo', 'bar')])

等しいかどうかを確認したい:

>>> d1 == d2
True
>>> d1.keys() == d2.keys()
True

だが:

>>> d1.values() == d2.values()
False

値が等しくない理由を知っていますか?

これをPython 3.4および3.5でテストしました。


この質問に続いて、私はPython-Ideasメーリングリストに投稿して詳細を追加しました:

https://mail.python.org/pipermail/python-ideas/2015-December/037472.html

51
Alexandre

Python 3、で、dict.keys()およびdict.values()は、それぞれcollections.abc.KeysViewおよびcollections.abc.ValuesViewの特別な反復可能なクラスを返します。最初の1つはsetから__eq__メソッドを継承し、2つ目はオブジェクトのIDをテストするデフォルトのobject.__eq__を使用します。

41

Python3では、d1.values()d2.values()collections.abc.ValuesViewオブジェクトです:

>>> d1.values()
ValuesView(OrderedDict([('foo', 'bar')]))

それらをオブジェクトとして比較しないでください。それらをリストに変換してから比較します。

>>> list(d1.values()) == list(d2.values())
True

キーを比較するために機能する理由を調査します。CPythonの_collections_abc.pyでは、KeysViewSetから継承していますが、ValuesViewは次のことを行いません:

class KeysView(MappingView, Set):

class ValuesView(MappingView):
  • ValuesViewとその親の__eq__のトレース:

    MappingView ==> Sized ==> ABCMeta ==> type ==> object

    __eq__objectにのみ実装され、オーバーライドされません。

  • 一方、KeysViewSetから直接__eq__を継承します。

20
BigOther

残念ながら、どちらの現在の回答もこれがなぜなのかについては触れていませんが、これがどのように行われるかに焦点を当てています。メーリングリストでの議論は素晴らしかったので、以下にまとめます。

odict.keys/dict.keysおよびodict.items/dict.itemsの場合:

  • odict.keysdict.keys のサブクラスは、collections.abc.Set(セットのようなオブジェクト)に準拠しているため、比較をサポートしています。これは、ディクショナリ内のkeys(順序付けされているかどうかにかかわらず)が一意でハッシュ可能であることが保証されているために可能です。
  • odict.itemsdict.items のサブクラス)も、.keysと同じ理由で比較をサポートします。 itemsviewは、items(具体的には、値を表す2番目の要素)の1つがハッシュ可能でない場合に適切なエラーを発生させるため、これを行うことができますが、一意性は保証されます(keysは一意です):

    >>> od = OrderedDict({'a': []})
    >>> set() & od.items()
    TypeErrorTraceback (most recent call last)
    <ipython-input-41-a5ec053d0eda> in <module>()
    ----> 1 set() & od.items()
    
    TypeError: unhashable type: 'list'
    

    これらの両方のビューkeysitemsの比較では、オブジェクトall_contained_inを使用する __contain__ (かなり読みやすい)と呼ばれる単純な関数を使用します関連するビューの要素のメンバーシップをチェックする方法。

odict.values/dict.valuesについて:

  • 気づいたように、odict.valuesdict.valuesのサブクラス [shocker])は次のように比較しませんセットのようなオブジェクト。これは、valuesvaluesviewをセットとして表すことができないためです。理由は2つあります。

    1. 最も重要なのは、ビューにドロップできない重複が含まれている可能性があることです。
    2. ビューにはハッシュ可能でないオブジェクトが含まれている可能性があります(それ自体では、ビューをセットのようなものとして扱わないのに十分ではありません)。

メーリングリストの @ user2357112 および @ abarnett によるコメントで述べられているように、odict.values/dict.valuesはマルチセットであり、セットの一般化です。その要素の複数のインスタンスを許可します。これらを比較しようとすることは、固有の重複、順序付け、およびこれらの値に対応するキーを考慮する必要があるという事実により、keysまたはitemsを比較するほど簡単ではありません。 dict_valuesは次のようになります。

>>> {1:1, 2:1, 3:2}.values()
dict_values([1, 1, 2])
>>> {1:1, 2:1, 10:2}.values()
dict_values([1, 1, 2])

キーに対応する値が同じでなくても、実際には等しいですか?多分?そうでないかもしれない?それはどちらの方法でも簡単ではなく、避けられない混乱を招きます。

ただし、これらをそのままkeysおよびitemsと比較して、要約すると、@ abarnettからの別のコメント mailingリスト

標準のマルチセットタイプまたはABCがないにもかかわらず、マルチセットが何をすべきかを定義し、それを値ビューに適用できると考えている場合、次の質問は、非ハッシュ可能な2次時間よりも優れた方法でそれを行う方法です。値。 (そして、ここでの順序付けも想定できません。)値ビューを30秒間ハングさせてから、20ミリ秒で間違った答えを返すのではなく、直感的に望んだ答えを返すのは改善でしょうか。 (どちらの方法でも、同じレッスンを学びます。値のビューを比較しないでください。20ミリ秒で学習したいと思います。)