[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 21:38:53 EST 2015


On Thursday, December 17, 2015 2:19 PM, Franklin? Lee <leewangzhong+python at gmail.com> wrote:

> > On Thu, Dec 17, 2015 at 4:34 PM, Andrew Barnert <abarnert at yahoo.com> 
> wrote:
>>  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.
> 
> Hm.
> 
>     >>> x = {0: []}
>     >>> y = {0: []}
>     >>> x == y
>     True
>     >>> x.items() == y.items()
>     True
> 
> That last one doesn't seem set-like to me. But it seems I
> misunderstood what you were saying.

Sure it's set-like

> Looking at the source code for ItemsView, "containment" is defined as
> "other[0] in self and self[other[0]] == other[1]". So yes, it's
> set-like, in that it checks for containment. I've just never thought
> about "containment of a key => value mapping".

Yeah, that's the point: items views are effectively sets of items, which are key-value pairs. What else would they be sets of?

Because values aren't necessarily hashable, it's unavoidable that some set operations may raise a TypeError (if the values aren't hashable) instead of doing what you'd want. But that doesn't mean all set operations that could possibly raise a TypeError always do so; that only happens when it's unavoidable. And here, it's avoidable.

I suppose you could argue that this is a bit too clever to just assume without documenting, and another Python implementation might well have a dict items view that just directly tried hash(self) and raised here, so you really can't write any code that depends on this behavior. Maybe that's true. But that still doesn't seem like a problem with CPython's implementation; the question would just be how to change the docs (whether to require other implementations to do the same thing or to explicitly allow them to raise).

> (It's funny, 
> 'cause
> I've tried to develop the exact same idea in a different subject.)


>>>>>   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?
> 
> I mean for them to raise NotImplemented in the case of "the other dict
> is not an instance of OrderedDict".
> 
> 
> 
> Anyway, this is all a moot point. *If* they were to do something
> different from dict's views, then they should follow
> OrderedDict.__eq__.

Sure; any other option is terrible.

In fact, both of these options are terrible--one breaks consistency with other mappings, and with the basic rules of comparison; the other breaks consistency with the other related types. Ick. Then again, they both have compelling arguments for them, and they're both pretty simple. I doubt anyone has any critical code that relies on the current behavior, but then I doubt anyone would write any critical code that relied on the other behavior if it were changed.

To me, it looks like the best deciding factor is inertia. If it's worked the same way for years, it might as well keep working that way. Maybe add a note in the docs saying you shouldn't compare the things, especially to other mapping types' views, and what will happen if you do?


More information about the Python-ideas mailing list