Почему значения 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
Ответы
Ответ 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 миллисекундах.)