2015-12-16 4 views
47

С Python 3:Почему значения OrderedDict не равны?

>>> 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-идей список рассылки, чтобы иметь дополнительные детали:

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

+0

Работает хорошо на Python 2.7.6 –

+6

** 'ДИКТ. values' ** возвращает [** 'ValuesView' **] (https://docs.python.org/3/glossary.html#term-dictionary-view) –

ответ

36

В Python 3, dict.keys() и dict.values() возврата специальных Iterable классов - соответственно collections.abc.KeysView и collections.abc.ValuesView. Первый наследует метод __eq__ от set, второй использует значение по умолчанию object.__eq__, которое проверяет идентификатор объекта.

+4

Кажется, это дефект для меня. Вы случайно не знаете, было ли решение не переопределять '__eq__' было намеренным или просто надзором? –

+3

@RobKennedy не самая сложная идея - но Python - это бесплатное программное обеспечение, поэтому ничто не мешает вам отправлять патч;) –

+6

Я думаю, что решение не переопределять 'object .__ eq__' имеет смысл, потому что это неоднозначно, что вы сравниваете. Должно ли два значения ValueView 'равны, если они содержат одни и те же значения в любом порядке или нет? Если это не зависит от ключей или нет? В зависимости от того, что вы хотите, гораздо проще сравнивать '.items()' или поместить значения в список или установить и сравнить их. –

23

В Python3, d1.values() и d2.values() являются collections.abc.ValuesView объекты:

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

Не сравнить их как объект, с onvert их в списки, а затем сравнить их:

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

Исследуя, почему это работает для сравнения ключей, в _collections_abc.py из CPython, KeysView наследуется от Set в то время как ValuesView не:

class KeysView(MappingView, Set): 

class ValuesView(MappingView): 
  • Трассировка __eq__ в ValuesView и его родителями:

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

    __eq__ выполнен только в object и не переопределен.

  • С другой стороны, KeysView наследует __eq__ напрямую от Set.

+2

' values ​​() 'возвращает итератор на python 3 , и список в python 2.7 – bgusach

+5

@bgusach это не итератор, но он повторяется. См. [Просмотр объектов] (https://docs.python.org/3/library/stdtypes.html#dict-views). –

+2

@PeterWood, я думал, что 'values' был эквивалентом' itervalues', но вы правы, это не так. – bgusach

2

К сожалению, в обоих текущих ответах не рассматриваются причины, по которым это делается, но сосредоточиться на том, как это делается.Этот список рассылки обсуждение было удивительно, так что я буду суммировать вещи:

Для odict.keys/dict.keys и odict.items/dict.items:

  • odict.keys (subclass of dict.keys) поддерживает сравнение из-за его соответствие collections.abc.Set (это набор подобный объект). Это возможно из-за того, что keys в словаре (заказанный или нет) гарантированно будет уникальным и хешируемым.
  • odict.items (subclass of dict.items) также поддерживает сравнение по той же причине, что и .keys. itemsview позволено сделать это, так как он поднимает соответствующую ошибку, если один из item с (в частности, второй элемент, представляющий собой значение) не hashable, уникальность гарантируется, хотя (из-за 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' 
    

    Для обоих этих видов keys, items сравнение использует простую функцию, называемую all_contained_in (довольно читабельна), которая использует объекты __contain__ метод для проверки принадлежности элементов в рассматриваемых представлениях.

Теперь о odict.values/dict.values:

  • Как заметил, odict.values (subclass of dict.values [шокер]) не сравнивать как подобные множества объектов. Это происходит потому, что values из valuesview не может быть представлена ​​в виде набора, причины двоякая:

    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 на the mailing list:

Если вы думая, что мы могли бы определить, какие мультимножества должны делать, несмотря на то, что у них нет стандартного мультимножества или ABC для них, и примените это к значениям представлений, следующий вопрос заключается в том, как сделать это лучше, чем квадратичное время для значений без хэширования. (И вы не можете взять на себя заказ здесь.) Будет ли иметь представление значений висеть в течение 30 секунд, а затем вернуться с ответом, который вы интуитивно хотели, вместо того, чтобы дать неправильный ответ в 20 миллиселей в качестве улучшения? (В любом случае, вы будете изучать один и тот же урок: не сравнить взгляды ценности Я предпочел бы узнать, что в 20 мс с.).