As I understand it, the only reason you want this ABC is so that if someone uses your struct-sequence class and defines his attributes with no inherent order, you want to be able to raise an error if he uses positional initialization. Has anyone ever done that? Has anyone complained "I created a struct type with attributes a and b in arbitrary order, and then when I do Spam(1, 2), sometimes a gets 1 and sometimes it gets 2"?

It seems to me that this is obviously the same as, say, a user explicitly zipping two sets together. Sure, it's a stupid thing to do—but it's an _obvious_ stupid thing to do, so zip doesn't have to prevent them from doing it or go out of its way to warn them. And it's the same here. The order of the attributes is the order of the slots, so if you specify them in arbitrary order, that will be an arbitrary order, obviously.

Maybe there's something about this use case that causes people to not notice they've screwed up like this? If so, I think that's what you have to make a case for. (That's why I asked whether anyone has actually complained about it.)

One more thing: what's stopping you from defining Ordered in your module and registering all the relevant builtin types there? In fact, you pretty much have to do this even if your proposal is accepted, unless you want your module to require 3.6+ (which seems unlikely, given that you're using six). So what would you gain by having it in the stdlib?

On Nov 6, 2015, at 14:11, Amir Rachum <amir@rachum.com> wrote:

I am suggesting the addition of a collections abstract base class called "Ordered". Its meaning is that a collection's iteration order is part of its API. The bulk of this mail describes a use case for this. The reason I believe that such abstract base class is required is that there is no way to test this behavior in a given class. 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.)


As examples, set, frozenset, dict and defaultdict should *not* be considered as ordered. list, OrderedDict, deque and tuple should be considered ordered.


Before I dive into the use case I am presenting, I would like to point out that a similar discussion was already done on the subject (suggesting at first that OrderedDict would abstract-subclass from Sequence) at the following thread* - http://code.activestate.com/lists/python-ideas/29532/. That thread was abandoned largely from a lack of a clear use case, which I hope will be more clear in this suggestion.


I am working on package called basicstruct (https://pypi.python.org/pypi/basicstruct). The way it works is that you define an object that inherits from basicstruct.BasicStruct, define __slots__ , and automatically get behaviors such as a nice __init__, __str__, comparison functions, attribute access, dict-like access, pickleability, etc. It is similar to namedtuple, except that it is mutable.


In the Python documentation, the following is said regarding __slots__:


   Any non-string iterable may be assigned to __slots__. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key.


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


   class BasicStruct(object):

       """Class for holding struct-like objects."""


       __slots__ = ()  # should be extended by deriving classes


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

           arg_pairs = zip(self.__slots__, args)

           for key, value in chain(arg_pairs, six.iteritems(kwargs)):

               setattr(self, key, value)


           for key in self.__slots__:

               if not hasattr(self, key):

                   setattr(self, key, None)


Notice the following line:

   

   arg_pairs = zip(self.__slots__, args)


It assumes that __slots__ defines attributes in a certain order. So as a use I would expect that the following code


   class MyStruct(BasicStruct):

      __slots__ = ('x', 'y')

   

   MyStruct(0, 1)


... will create a struct in which x is 0 and y is 1.


However, if I define __slots__ as a set, or dict, the result is undefined.


   class MyStruct(BasicStruct):

      __slots__ = {'x', 'y'}  # No order is defined here

   

   MyStruct(0, 1)  # Which is which?


So, In BasicStruct's __init__ method it is required to test whether __slots__ was defined with a collection that has a meaningful order. If it was _not_, using non-keyword arguments in __init__ should be forbidden, since the order of __slots__ is arbitrary. Here is how I wish the code would look like:


   class BasicStruct(object):

       """Class for holding struct-like objects."""

   

       __slots__ = ()  # should be extended by deriving classes

   

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

           ordered = isinstance(self.__slots__, Ordered)

   

           if args and not ordered:

               raise ValueError("Can't pass non-keyword arguments to {}, since "

                                "__slots__ was declared with an unordered "

                                "iterable.".format(self.__class__.__name__))

   

           arg_pairs = zip(self.__slots__, args)

           for key, value in chain(arg_pairs, six.iteritems(kwargs)):

               setattr(self, key, value)

   

           for key in self.__slots__:

               if not hasattr(self, key):

                   setattr(self, key, None)


Thanks,

Amir Rachum




* The author of the original thread is Ram Rachum. To avoid some confusion - yes, we are related - Ram is my big brother. It's just so happens that I was looking around for this issue and found that he did so in the past as well.


_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/