Почему значения 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
не может быть представлено как множество, причины двояки:- Что наиболее важно, представление может содержать дубликаты, которые нельзя отбросить.
- Представление может содержать не хэшируемые объекты (что само по себе недостаточно для того, чтобы не рассматривать представление как набор-подобный).
Как указано в комментарии 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 миллис.)