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/