Equivalence of view objects of Python dict

Viewed 307

View objects for Python dict compare differently than I would expect:

a = {0 : 'a', 1 : 'b'}
b = {1 : 'b', 0 : 'a'}

print(a == b) # True
print(a.keys() == b.keys()) # True
print(a.values() == b.values()) # False
print(a.items() == b.items()) # True

Is there any particular reason why for dict.values() this is False?

In fact, in the case of same dicts (not even the copy), it is also False:

a = {0 : 'a', 1 : 'b'}
print(a.values() == a.values()) # False

What does the equality of this view object mean then?

3 Answers

Dictionary view classes implement their own equality method. The documentation of dict.values() specifically says:

An equality comparison between one dict.values() view and another will always return False. This also applies when comparing dict.values() to itself

For dict.keys() and dict.items(), the documentation of dictionary views says:

Keys views are set-like since their entries are unique and hashable. If all values are hashable, so that (key, value) pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not unique.) For set-like views, all of the operations defined for the abstract base class collections.abc.Set are available (for example, ==, <, or ^).

keys and items views are set-like (or mostly set-like for items views with non-hashable values) - they behave like set objects in many ways, and particularly, it's easy to perform in tests on such views. That lets those views support an efficient == operation based on whether the two views contain the same elements.

For values views, there's no good way to implement such an == operation, so values views don't implement anything fancy for ==. They just inherit the default __eq__ implementation from object, so two values views will only be considered equal if they're the same object. Even for two views of the same dict, you'll only get True if they're actually the same view object:

In [2]: x = {}

In [3]: x.values() == x.values()
Out[3]: False

In [4]: v = x.values()

In [5]: v == v
Out[5]: True

dict is un-ordered, meaning that as long as all keys in a exist in b and all keys in b exist in a and all values for those keys are equal, the two sets are equal.

If you're instead trying to check if a and b are the same instance, then you would use is.

>>> a = {0 : 'a', 1 : 'b'}
>>> b = {1 : 'b', 0 : 'a'}

>>> a is b
False

>>> a = {0 : 'a', 1 : 'b'}
>>> b = a

>>> a is b
True

As for the question about why a.values() == b.values() returns False, this is a choice made in the Python language. A quote taken from the Python docs:

An equality comparison between one dict.values() view and another will always return False. This also applies when comparing dict.values() to itself:

>>> d = {'a': 1}
>>> d.values() == d.values()
False
Related