Inconsistent behaviour between dict.values() and d

2020-06-30 09:33发布

问题:

I have found that comparing the results of the keys() and values() methods of the dict built-in to themselves results in inconsistent results:

instance = {'one': 1}

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

Running the above code in Python 2.7 will return True for both calls, leading me to believe that there is some implementation detail in Python 3's dict_values that causes this strange behaviour.

Is there a reason for this behaviour or have i stumbled upon some obscure bug?

回答1:

The short answer: class dict_values doesn't have a __eq__ method implemented, but class dict_keys does:

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

Therefore, the d.values()'s == comparison evaluates to False.

The longer answer of why it wasn't implemented is a different one and can be seen with a little more digging on the documentation of dict-view objects. This part seems especially relevant (emphasis mine):

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

Since keys must be unique, it makes sense that they are set-like and are supported with the class operations of collections.Set. Values are not set-like due to non-uniqueness.

In Python 2.7 however, d.keys() and d.values() both return a list per the documentation therefore this restriction does not apply. Since they are both the same type of object, it makes sense the same operation will work on both. If you used viewkeys and viewvalues as mentioned in the documentation of dict-view objects in Python2.7, then you can expect similar behaviour:

# 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