[Python-ideas] Fwd: Why do equality tests between OrderedDict keys/values views behave not as expected?

Andrew Barnert abarnert at yahoo.com
Thu Dec 17 16:34:29 EST 2015


On Thursday, December 17, 2015 11:50 AM, Franklin? Lee <leewangzhong+python at gmail.com> wrote:

> > On Thu, Dec 17, 2015 at 11:57 AM, Andrew Barnert <abarnert at yahoo.com> 
> wrote:
> 
>>  * since all mapping keys and items act like sets (and are Sets), they 
> probably compare like sets
> 
> .items() aren't like sets. Or something is very wrong.

Yes they are. Look at the library documentation for dict and dict views in stdtypes, and in collections.abc. An items view is supposed to be set-like, and to be actually usable as a set if the values are hashable. (If the values aren't hashable, obviously most set operations are just going to raise.) And, as far as I can tell, nothing is very wrong.

>>>  1. OrderedDict().values() does not implement __eq__. It uses object
>>>  equality, which means identity.
>>>  1a. dict().values() does not implement __eq__.
>>> 
>>>  2. OrderedDict().keys().__eq__ does not respect order.
>>> 
>>>  I'd argue that keys() should be ordered comparisons, and values()
>>>  could be.
>> 
>>  So what happens when you compare the keys, items, or values view from an 
> OrderedDict against the view from another mapping type? Or, for keys and items, 
> against another set type? If you leave that up to the whichever one is on the 
> left, you get cases where a==b and b!=a. If you leave it up to the most derived 
> type (by the usual __rspam__-like rules), that doesn't help anything, since 
> dict_keys_view and odict_keys_view are unrelated except in sharing an abstract 
> base class. And, worst of all, even if you contrive a way for one or the other 
> to always win consistently, you get cases where a==b and b==c and a!=c.
> 
> If OrderedDict views raised NotImplemented, I believe the other view
> will then have the chance to try its own comparison.

Yes, but the proposal here is for OrderedDict and its views to implement something sequence-like, not to raise NotImplemented, so why is that relevant?

>>>  As a plus, this could be more efficient than unordered
>>>  comparisons, since it's just
>>>     return all(x == y for x, y in zip(self.keys(), other.keys()))
>>>  instead of packing each into a set and comparing the sets.
>> 
>>  I think you want zip_longest(self.keys(), other.keys(), fill=object()) or 
> equivalent; otherwise {1, 2} will be equal to {1, 2, 3} .
> 
> I actually want
>     return len(self.keys()) == len(other.keys()) and all(x == y for x,
> y in zip(self.keys(), other.keys()))

Which is something equivalent--the only way that can return different results is if self.values() contains elements that declare themselves to be equal to everything, at which point they already break too many invariants to safely put in a container.


More information about the Python-ideas mailing list