Несовместимое поведение между равенством dict.values() и dict.keys() в Python 3.x и Python 2.7

Я обнаружил, что сравнение результатов методов keys() и values() встроенного в себя dict приводит к противоречивым результатам:

instance = {'one': 1}

instance.values() == instance.values() # Returns False
instance.keys() == instance.keys()     # Returns True

Выполнение приведенного выше кода в Python 2.7 вернет True для обоих вызовов, что наводит меня на dict_values что в Python 3 есть некоторые детали реализации dict_values, вызывающие это странное поведение.

Есть ли причина для такого поведения, или я наткнулся на какую-то неясную ошибку?

Ответы

Ответ 1

Краткий ответ: class dict_values не имеет __eq__ метода __eq__, но class dict_keys делает:

>>> d.values().__eq__(d.values())
NotImplemented
>>> d.keys().__eq__(d.keys())
True

Поэтому сравнение d.values() == оценивается как False.

Более длинный ответ о том, почему он не был реализован, - другой, и его можно увидеть, немного покопавшись в документации объектов dict-view. Эта часть кажется особенно актуальной (выделено мной):

Представления ключей похожи на наборы, поскольку их записи уникальны и доступны для хэширования. Если все значения могут быть хэшируемыми, поэтому пары (ключ, значение) уникальны и хэшируемы, то представление элементов также задается как набор. (Представления значений не обрабатываются как подобные множеству, так как записи, как правило, не являются уникальными.) Для представлений, подобных множеству, доступны все операции, определенные для collections.abc.Set абстрактного базового класса .abc.Set (например, ==, < или ^).

Поскольку ключи должны быть уникальными, имеет смысл, что они подобны множеству и поддерживаются операциями с классами collections.Set. Значения не установлены как из-за неединственности.

Однако в Python 2.7 и d.keys() и d.values() возвращают list соответствии с документацией, поэтому это ограничение не применяется. Поскольку оба объекта относятся к одному и тому же типу, имеет смысл использовать одну и ту же операцию для обоих. Если вы использовали viewkeys и viewvalues как упомянуто в документации объектов dict-view в Python2.7, то вы можете ожидать аналогичного поведения:

# Python 2.7
from collections import Set
# in Python 3.x this would be from collections.abc import Set

d = {"one": 1}

print isinstance(d.viewkeys(), Set)
# True

print isinstance(d.viewvalues(), Set)
# False

print d.viewkeys() == d.viewkeys()
# True

print d.viewvalues() == d.viewvalues()
# False