On Sun, Jul 19, 2020 at 9:53 PM Stephan Hoyer <shoyer@gmail.com> wrote:
On Fri, Jul 17, 2020 at 9:22 PM Ricky Teachey <ricky@teachey.org> wrote:
>>> # The positional arguments aren't part of the KeyObject
>>> d[a, b:c, d, e=5, f=6] == d.__getitem__((a, b:c, d), KeyObject(e=5, f=6))

This raises a question that needs to be answered, then: what would be the utility of mixing together positional and kwd arguments in this way?

Even the xarray examples given so far don't seem to make use of this mixture. From my knowledge of pandas I am not sure what the meaning of this would be, either.

One use case that comes up in xarray and pandas is support for indicating indexing "modes". For example, when indexing with floating point numbers it's convenient to be able to opt-in to approximate indexing, e.g., something like:
array.loc[longitude, latitude, method='nearest', tolerance=0.001]

Thanks to those who pointed out that using kwargs to specify modes of key management is an obvious use case.

Additionally I agree it is extremely likely that most use cases will involve specific kwd args, as Steven argues:

On Sat, Jul 18, 2020 at 1:49 PM Steven D'Aprano <steve@pearwood.info> wrote:

Most use-cases I can think of will have to unpack the dict into named
parameters. (Just as the interpreter does for us, in function calls.)

When I'm writing functions, for every one use of `**kwargs`, I have
about a hundred uses of named parameters. I'm pretty sure most people
are similar. I don't think that keyword args in subscripts will be
different. I'm pretty sure that nearly everyone will want to unpack the
`**kwargs` into named parameters nearly all of the time.

So why force them to do the unpacking themselves when the interpreter
already has all the machinery to do it?

And Guido and Christopher Barker agree:

On Sun, Jul 19, 2020 at 12:46 AM Guido van Rossum <guido@python.org> wrote:
On Sat, Jul 18, 2020 at 9:11 PM Christopher Barker <pythonchb@gmail.com> wrote:

On Sat, Jul 18, 2020 at 1:43 PM Guido van Rossum <guido@python.org> wrote:
Yes please.

Yes to what, exactly?

Not to this...

I don't think that is either simpler or more useful than a straight-
forward binding of arguments to parameters, just as function calls
already do:

    obj[a, b:c, x=1] ==> obj.__getitem__((a, slice(b, c)), x=1)

But to this.

It's unfortunate that positional subscripts are bundled together into a
tuple. I have resisted calling that design a "mistake" because I don't
know the reason for the design. There was probably a good reason for it,
back in the ancient history of Python when getslice and getitem were
unified. But I am sure that it will be a horrible mistake to emulate
that decision for keyword arguments.

If anyone wants or needs their keyword arguments to be bundled into a
single kwargs parameter, you can have it. All you need do is declare
your method with a `**kwargs` parameter, and the interpreter will do the

These two paragraphs make it clear what Steven was proposing. I am supporting him in this.

So it's a very good point and I support the idea.

Steven replied to my concern about the inconsistent mental model:

On Sat, Jul 18, 2020 at 5:09 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Jul 18, 2020 at 12:18:38AM -0400, Ricky Teachey wrote:
> This strikes me as problematic for having a consistent mental model of how
> stuff works in python. I think that for many the difference in the meaning
> of the syntax between item-getting/setting and function-calling would be...
> glaring.

Yes, but what are we going to do about it?


We have a few choices:


(3) Reinforce that inconsistency, and continue to obfuscate the
similarities, by handling keyword arguments in the same fashion as
comma-separated subscripts. This will require a new builtin "key-object"
class, and it will require every class that cares about keyword
arguments in their subscripts to parse them themselves.

We'll also need to decide how to combine subscripts and keywords:

    obj[a, b:c, x=1]
    # is this a tuple argument (a, slice(b, c), key(x=1))
    # or key argument key(a, slice(b, c), x=1)

So it's worth reiterating that parsing of the keyword arguments can be accomplished relatively easily using a decorator that groups the arguments into some sort of key object, as Jonathan Fine is suggesting.

On Sat, Jul 18, 2020 at 7:50 AM Jonathan Fine <jfine2358@gmail.com> wrote:

In some other situations we wish for
    >>> d[1, 2, x=3, y=4] = 5
to result in a call such as
    >>>> __setitem__(d, (1, 2), 5, x=3, y=4)
where __setitem__ is a function defined in the implementation of D = type(d).

I fully support this goal, although not the implementation details in the example above. It is my opinion that this goal is best achieved by making easier TRANSPARENT use of 
    k = K(1, 2, x=3, y=4)

Here's how it goes. First we write
    class D:
        def __setitem__(self, val, u, v, x, y):
            pass  # Or do something.

Next, we define wibble. It will be a SIGNATURE CHANGING ADAPTER.  Those who know how to make decorators will, I hope, have little difficulty in defining wibble to do what is required. For this exercise, assume that k.argv = (1, 2), and k.kwargs = dict(x=3, y=4).

The main idea is that each class will make an opaque use of the key, unless it uses a signature changing adapter to enable a transparent use of the key.  Thus, by default key use is opaque, but if a class wishes it can make transparent use.

Without examples and working code (which I've promised for the end of the month), this might be hard to understand. However this is I hope clear enough for now. 

So anyone who wants to define their arguments ungrouped (ie, as separate positional args OR as kwd names) can group them into a key object the way the dunder methods currently expect like this:

class C:
    def __getitem__(self, pos1, pos2="foo", *, kwd1, kwd2="bar"):
        return ...
    def __setitem__(self, value, pos1, pos2="foo", *, kwd1, kwd2="bar"):

Such a decorator could be added to the standard lib. I'm not a C expert, but I'm assuming it could be written in C so that it is as fast as the regular function call machinery.

The function generated by groupify/wibble would have the same signature as current __getitem__ or __setitem__ methods. And the positional key argument would be passed the KeyObject, or K, instance. The decorator machinery would break apart the K object and pass it into the decorated function the way Guido, Christopher B, Steven D, et al desire. No change to the current signatures needed, and no extremely odd mental model to deal with.

I do not know if this is the best way forward or not. But I think the pros and cons ought to be thoroughly considered as an option. I don't feel qualified to do all the weighing, but it seems to make a lot of sense to me.