Почему значения 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

3 ответа

Решение

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

В python3 d1.values() а также d2.values() являются collections.abc.ValuesView объекты:

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

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

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

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

За odict.keys / dict.keys а также odict.items / dict.items:

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

    1. Что наиболее важно, представление может содержать дубликаты, которые нельзя отбросить.
    2. Представление может содержать не хэшируемые объекты (что само по себе недостаточно для того, чтобы не рассматривать представление как набор-подобный).

Как указано в комментарии user2357112 и abarnert в списке рассылки, 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 в списке рассылки:

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

Другие вопросы по тегам