Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

Ответ 2

В python3, d1.values() и d2.values() находятся объекты collections.abc.ValuesView:

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

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

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

Ответ 3

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

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

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

    • Самое главное, что представление может содержать дубликаты, которые нельзя удалить.
    • В представлении могут содержаться объекты, не содержащие хэширования (которые сами по себе недостаточны, чтобы не обрабатывать представление как заданное).

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

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