On 8 Nov 2015, at 20:06, Amir Rachum <amir@rachum.com> wrote:

I would like to expand on my original use case, which I hope will provide a more persuading argument.
As part of BasicStruct I intend to allow the use of mapping types as __slots__, with the semantics of default values.
So it'll look something like this:

    class Point(BasicStruct):
        __slots__ = {'x': 5, 'y': 7}

So instead they'll write
    __slots__ = OrderedDict({'x': 5, 'y': 7})
Causing the same issues?


My fear is that someone will just use a dict literal as stated above and try to create an object with positional arguments:

    Point(0)

The problem is that iteration order over a dict is not guaranteed, so this might initialize Point(x=0, y=7) like the use intends, or Point(x=5, y=0). So in this case I want to disable positional arguments. However, positional arguments for initializing struct are pretty convenient, so I would like to make that available as far as possible. An implementation using Ordered might look like this:

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

        __slots__ = ()  # should be extended by deriving classes

        def __init__(self, *args, **kwargs):
            default_values = isinstance(self.__slots__, Mapping)
            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):
                    default_value = None
                    if default_values:
                        default_value = self.__slots__[key]
                    setattr(self, key, default_value)


This will allow users who are interested in positional argument initialization to use an Ordered mapping (such as OrderedDict, or a custom collection).
I would like to emphasize again that the most compelling reason for me that this should be part of the stdlib is that there is no workaround for this that also allows users to use ordered custom collections. There is no way to check for "promised ordering" in any functional manner.

Besides the fact that you can create that base-class yourself, and register OrderedDict as an instance of it already, I think there's a better solution to your problem (not quite tested, but should work):

1. Create a Field class which maintains a creation-counter to be able to query ordering.
2. In the __init__ check against the creation ordering.

Added bonus: you can choose to have some attributes have a default value, and others not having a default value. 

If you're willing to do some metaclass trickery, you could even end up with a syntax like:

    class Point(Struct):
        x = Field(5)
        y = Field(7)
        z = Field()  # we force the user to supply the z value, as it has no default. 

And generate the __slots__ from that. Maybe look at Django's ModelBase implementation, and how it uses metaclasses for that?

Even more added bonus would be defining special Field sub-classes which also check the type/value of the supplied values (NumericField raising ValueError when you pass it the string "foo").

Not really sure what the added benefit of an Ordered base-class would be here.

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