Why does the OrderedDict keys view compare order-insensitive?
Question: Why does the `OrderedDict` keys view compare order-insensitive?

    
    
    >>> from collections import OrderedDict
    >>> xy = OrderedDict([('x', None), ('y', None)])
    >>> yx = OrderedDict([('y', None), ('x', None)])
    >>> xy == yx
    False
    >>> xy.keys() == yx.keys()
    True
    

The OrderedDict keys view should arguably behave like an OrderedSet, but
instead it behaves the same as `dict.keys` (i.e. like a usual `set`).

Same "issue" in python2:

    
    
    >>> xy.viewkeys() == yx.viewkeys()
    True
    

They are different types, (`odict_keys` is a subclass of `dict_keys`)

    
    
    >>> type(xy.keys())
    odict_keys
    >>> type({}.keys())
    dict_keys
    

And there is already an [order-sensitive keys comparison
available](https://github.com/python/cpython/blob/master/Objects/odictobject.c#L794)
that they could have trivially used, but it's apparently only used as a [post-
check](https://github.com/python/cpython/blob/master/Objects/odictobject.c#L1604)
for the odict rich comparison.

Is this a design decision, or a bug? If it's a design decision, where could I
find a discussion of the justification?


Answer: Looks like `OrderedDict` delegates the implementation of the various view
objects to the common `dict` implementation; this remains the case even in
Python 3.5 where `OrderedDict` gained a C accelerated implementation ([it
delegates object construction to
`_PyDictView_New`](https://hg.python.org/cpython/file/e81189f75d04/Objects/odictobject.c#l2119)
and [provides no
override](https://hg.python.org/cpython/file/e81189f75d04/Objects/odictobject.c#l2176)
for the generic view's [rich comparison
function](https://hg.python.org/cpython/file/e81189f75d04/Objects/dictobject.c#l3332).

Basically, `OrderedDict` views iterate with the same order their backing
`OrderedDict` would (because there is no cost to do so), but for `set`-like
operations, they act like `set`, using content equality, subset/superset
checks, etc.

This makes the choice to ignore ordering make sense to some extent; for some
`set` operations (e.g. `&`, `|`, `^`), the return value is a `set` without
order (because there is no `OrderedSet`, and even if there were, which
ordering do you use for something like `&` where the ordering may be different
in each view?), you'd get inconsistent behaviors if some of the `set`-like
operations were order sensitive and some weren't. And it would be even weirder
when two `OrderedDict` keys views were order sensitive, but comparing
`OrderedDict` views to `dict` views wasn't.

As I noted in the comments, you can get order sensitive `keys` comparison
pretty easily with:

    
    
    from operator import eq
    
    # Verify that keys are the same length and same set of values first for speed
    # The `all` check then verifies that the known identical keys appear in the
    # same order.
    xy.keys() == yx.keys() and all(map(eq, xy, yx))
    
    # If you expect equality to occur more often than not, you can save a little
    # work in the "are equal" case in exchange for costing a little time in the
    # "not even equal ignoring order case" by only checking length, not keys equality:
    len(xy) == len(yz) and all(map(eq, xy, yx))
    

