2015-12-16 20 views
47

z Python 3:Dlaczego wartości OrderedDict nie są równe?

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

Chciałem sprawdzić równość:

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

Ale:

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

Czy wiesz, dlaczego wartości nie są równe?

Testowałem to w Pythonie 3.4 i 3.5.


Po tym pytaniu Zamieściłem na pytona Pomysły liście dyskusyjnej mieć dodatkowe dane:

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

+0

Działa dobrze na Pythonie 2.7.6 –

+6

** 'dykt. values' ** zwraca wartość [** 'ValuesView' **] (https://docs.python.org/3/glossary.html#term-dictionary-view) –

Odpowiedz

36

W Pythonie 3 dict.keys() i dict.values() powrotne klasach specjalnych iterable - odpowiednio collections.abc.KeysView a collections.abc.ValuesView. Pierwszy dziedziczy po nazwie __eq__ z set, drugi używa domyślnego object.__eq__, który testuje tożsamość obiektu.

+4

Wygląda jak usterka. Czy zdajesz sobie sprawę, czy decyzja, aby nie uchybić wyrażenia "__eq__" nie była zamierzona, czy tylko nadzorem? –

+3

@RobKennedy nie jest pomysłem sligthtest - ale Python jest wolnym oprogramowaniem, więc nic nie stoi na przeszkodzie, aby przesłać poprawkę;) –

+6

Myślę, że decyzja, aby nie przesłonić 'obiektu .__ eq__' ma sens, ponieważ jest niejednoznaczna, co porównujesz. Czy dwa "ValueView" powinny być równe, jeśli zawierają te same wartości w dowolnej kolejności, czy też nie? Czy powinno to być niezależne od kluczy, czy nie? W zależności od tego, co chcesz, znacznie wyraźniej jest porównać '.items()' lub umieścić wartości na liście lub ustawić i porównać je. –

23

W python3, d1.values() i d2.values()collections.abc.ValuesView obiekty:

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

Nie porównuj je jako obiekt, c onvert nich list, a następnie porównać je:

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

Badanie dlaczego to działa na klucze porównywanie, w _collections_abc.py z CPython, KeysView dziedziczy z Set podczas ValuesView nie:

class KeysView(MappingView, Set): 

class ValuesView(MappingView): 
  • Kalka dla __eq__ w ValuesView i jego rodzicach:

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

    __eq__ jest zaimplementowany tylko w object i nie zostanie zastąpiony.

  • Z drugiej strony KeysView dziedziczy bezpośrednio od Set.

+2

' values ​​() 'zwraca iterator na pythonie 3 i lista w pythonie 2.7 – bgusach

+5

@bgusach to nie jest iterator, ale jest iterable. Zobacz [wyświetl obiekty] (https://docs.python.org/3/library/stdtypes.html#dict-views). –

+2

@PeterWood, Myślałem, że 'wartości' jest odpowiednikiem' itervalues', ale masz rację, nie są. – bgusach

2

Niestety, obie aktualne odpowiedzi nie wyjaśniają, dlaczego tak jest, ale koncentrują się na tym, jak to się robi.Że lista dyskusyjna dyskusja była niesamowita, więc będę Podsumowując rzeczy:

Dla odict.keys/dict.keys i odict.items/dict.items:

  • odict.keys (subclass of dict.keys) obsługuje porównania ze względu na jego zgodność z collections.abc.Set (jest to zestaw podobny obiekt). Jest to możliwe dzięki temu, że keys w słowniku (uporządkowanym lub nie) jest gwarantowane, że jest unikalne i niehieralne.
  • odict.items (subclass of dict.items) obsługuje również porównanie z tego samego powodu co .keys. itemsview może to zrobić, ponieważ podnosi odpowiedni błąd, jeśli jeden z item s (konkretnie, drugi element reprezentujący wartość) nie jest hashable, wyjątkowość jest gwarantowany, choć (ze względu na keys jest unikatowy):

    >>> 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' 
    

    Dla obu tych widoków keys, items, porównanie wykorzystuje prostą funkcję o nazwie all_contained_in (całkiem czytelna), która wykorzystuje metodę obiektów __contain__ do sprawdzania członkostwa elementów w widokach.

Teraz o odict.values/dict.values:

  • Jak zauważył, odict.values (subclass of dict.values [szok]) nie porównać jak przedmiot podobny do zadanej. To dlatego, że values z valuesview nie może być przedstawiony jako zbiór, powody są dwojakie:

    1. Co najważniejsze, widok może zawierać duplikatów, które nie mogą być pominięte.
    2. Widok może zawierać nieściągalne obiekty (które same w sobie nie wystarczą do tego, aby widok nie był traktowany jako podobny).

Jak podano w komentarzu przez @user2357112 i @abarnett na liście mailingowej, odict.values/dict.values jest multiset, uogólnienie zbiorów, która pozwala wiele wystąpień na jego elementy. Próba porównania nie jest tak banalna jak porównanie keys lub items z powodu nieodłącznego powielania, porządku i faktu, że prawdopodobnie trzeba wziąć pod uwagę klucze, które odpowiadają tym wartościom. Powinien dict_values że wyglądać tak:

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

faktycznie równe, chociaż wartości, które odpowiadają kluczy nie jest taki sam? Może? Może nie? Nie jest to ani jednoznaczne i doprowadzi do nieuchronnego zamieszania.

Punktem być wykonane jest jednak, że to nie jest trywialne, aby porównać je z keys jak i items, podsumowując, z innym komentarzem @abarnett na the mailing list:

Jeśli jesteś myśląc, że możemy zdefiniować to, co multisetyki powinny robić, pomimo braku standardowego typu multiset lub ABC dla nich, i zastosować to do wartości poglądów, następne pytanie brzmi: jak to zrobić w czasie lepszym niż czasy kwadratowe dla nie-stabilnych wartości. (I nie można również zakładać zamawiania tutaj.) Czy posiadanie widoku wartości zawiesiłoby się na 30 sekund, a następnie wróciłoby z odpowiedzią, której intuicyjnie potrzebowałeś, zamiast dać błędną odpowiedź w 20 milimitach, aby poprawić? (Tak czy inaczej, nauczymy się tej samej lekcji: nie porównuj wartości poglądów. Wolałbym nauczyć się tego w 20 milach.)

Powiązane problemy