[Python-ideas] Adding collections.abc.Ordered

Stephen J. Turnbull stephen at xemacs.org
Sat Nov 7 00:00:49 EST 2015


Amir Rachum writes:

 > An ordered collection has the exact same interface as an unordered
 > collection (e.g, dict and OrderedDict), other than a _promise_ of
 > the API that the order in which this collection will be iterated
 > has some sort of meaning (In OrderedDict, it is the order in which
 > keys were added to it.)

I think this is underspecified.  One of the main reasons why ordered
versions of initially unordered types, especially dict, take so long
to standardize is that people (even the Dutch!) disagree about the One
Obvious Order to impose.  After repeated refusals, only the factions
that continue clamoring- and preferably, only one -need be considered.

This ABC would be a tar pit of recriminations between class authors
and class clients.  At the very least, the author's intended
comparison function should be introspectable so that client subclasses
can include validation functions.  (I guess you could just use
"lambda slot: list(self.__slots__).index(slot)" as the key function,
but that doesn't say much about the conceptual semantics.)  Then the
presence of the ordering function would provide a way to duck-type
abc.Ordered.

I haven't thought carefully about it, but a method with semantics of
"insert next slot at end according to natural order" (named
"natural_append"?  "ordered_append"? or maybe the existing "append"
used by sequences could be given this semantic as well?

Nevertheless, I don't think this shold be added to Python yet.  I
don't think your use case is a valid one for supporting it.  I have
two reasons:

(1) In principle, record types should not be initialized with
    positional arguments.  I experienced cogniztive dissonance as soon
    as I saw your __init__():

    > Here's how the current__init__ method of BasicStruct looks like:

    >    class BasicStruct(object):
    >        """Class for holding struct-like objects."""

    "with naturally-ordered slots" should be appended.

    >        __slots__ = ()  # should be extended by deriving classes

    >        def __init__(self, *args, **kwargs):

    Yes, we do it in C all the time, and it works fine[1] there.  But
    the semantics are mismatched at the conceptual level, and it is
    error-prone in practice if the type has more than about 3 slots of
    differing types.  If a concrete BasicStruct "wants" to provide a
    positional constructor, it can use a factory function.

    Unfortunately this would be impossible to do automatically in the
    exactly the cases of interest -- you need the *source code* order
    of slots, but that's not available for introspection at runtime.
    So it would violate DRY.  I don't consider that an argument for
    the proposal at the moment, but feel free to try to convince the
    list (and maybe me too<wink/>) otherwise.)  (In fact I consider it
    the reverse: for Sequences, The Order You See Is The Order You
    Get, and the DRY violation occurs *because* you are using an
    inappropriate container for your purpose (ordered slots).

(2) BasicStruct isn't the relevant use case: you can always just
    document that __slots__ is restricted to ordered finite iterables
    except string-like ones which use sequences of length 1 to
    represent individual members, and perhaps trap some of the more
    obvious mistakes (set and dict and maybe their subclasses).

    The use case you need to present is one where a non-Sequence (eg,
    a set or dict) is the obvious representation for __slots__ in a
    BasicStruct.  Note that __slots__ allows nearly arbitrary finite
    iterables of strings only because there's no reason not to, and it
    simplifies the implementation.  In Python practice, that is indeed
    an invitation to experiment with concrete classes that use
    unordered __slots__ for some reason.  But there's no sense that
    abstract classes that use __slots__ in an abstract way (as yours
    does) must preserve that generality, and therefore no reason (yet)
    for Python to help you preserve that generality.


Footnotes: 
[1]  According to the usual definition of "works fine in C": is no
more than twice as error-prone as other features of C.  Given the
existence of pointers....



More information about the Python-ideas mailing list