On Tue, Jul 23, 2019 at 08:59:09PM -0000, Kristian Klette wrote: > Hi! > > During the sprints after EuroPython, I made an attempt at adding support for > comparing the results from `.values()` of two dicts. > > Currently the following works as expected: > > ``` > d = {'a': 1234} > > d.keys() == d.keys() > d.items() == d.items() > ``` > > but `d.values() == d.values()` does not return the expected > results. It always returns `False`. The symmetry is a bit off.
It seems to be doing an identity test on the "dict_values" view object itself: py> d = {'a': 1} py> a = b = d.values() py> a == b True which I expect is probably the default __eq__ inherited from object. Each time you call d.values() you get a distinct object, hence the False. I agree that this is a little surprising. Given that they are *views* of an underlying dict, I would expect that two views of the same dict ought to compare equal: assert d.values() == d.values() So at the least, we ought to have dict.values() comparison return True if the underlying dicts are identical. In pseudocode: def __eq__(self, other): if self is other: # Same object implies equality. return True if isinstance(other, Dict_Values_View_Type): if self.owner_dict is other.owner_dict: # Two views into the same dict are always equal. return True return NotImplemented I think that's the minimal behaviour that makes sense for a view. Beyond that, we start getting complicated, and potentially expensive. But I can suggest at least one useful invariant. If a, b are two dicts: a.items() == b.items() ought to be equivalent to: (a.keys() == b.keys()) and (a.values() == b.values) That implies something like this pseudo-code: def __eq__(self, other): if self is other: # Same object implies equality. return True if isinstance(other, Dict_Values_View_Type): a = self.owner_dict # dict we are taking a view of b = other.owner_dict if a is b: # Two views into the same dict are always equal. return True if len(a) != len(b): # Unequal lengths implies the values cannot be equal. return False if a.items() == b.items(): # (key,value) pairs are equal implies values are equal. return True elif a.keys() == b.keys(): # keys are equal but items are not equal implies # that the values must be different. return False # Fall back on value by value comparison? return list(self) == list(other) return NotImplemented > In the bug trackers[0] and the Github PR[1], I was asked > to raise the issue on the python-dev mailing list to find > a consensus on what comparing `.values()` should do. > > I'd argue that Python should compare the values as expected here, > or if we don't want to encourage that behaviour, maybe we should > consider raising an exception. Equality tests should (almost?) never raise an exception. It should be safe to test "are these two objects equal?" without guarding it in a try...except block. The definition of "equal" may not always be obvious, but it shouldn't raise unless the __eq__ method is buggy. In Python, its quite common for __eq__ to fall back on ``is``, e.g.: py> a = lambda x: x+1 py> a == a True py> a == (lambda x: x+1) False but I think in the case of views, we should at least fall back on identity of the underlying dicts, even if we decide the more complex tests are not worth the trouble. -- Steven _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/TT3NF3TFFRERELNJ6QCHRV2NBGW6EUN5/