On Sat, Sep 26, 2020 at 03:48:42PM -0700, Christopher Barker wrote:
> On Fri, Sep 25, 2020 at 11:50 PM Steven D'Aprano <st...@pearwood.info>
> wrote:
> >>
> >> 1. We have to pass a sentinel to the setitem dunder if there is no
> >> positional index passed.
> 
> 
> I still don't follow this logic -- why can't nothing be passed? The dunders
> either require an index or they don't, would that be just like function
> calling? So (adapting the example in the PEP:
>
> obj[spam=1, eggs=2]
> # calls type(obj).__getitem__(obj, spam=1, eggs=2)
> 
> This sure seems like the obvious way to handle it.

Christopher, with the greatest respect, it is really demoralising for me 
to explain this issue something like three, four, maybe five times now 
(I'm not going to go back and count), including this thread which is 
specifically about this issue, and then have people seemingly not even 
read it before writing back to disagree :-(

The problem isn't with the `__getindex__` dunder. It's the 
`__setindex__` dunder. I said it right there in the comment you quoted.

Okay, I was lazy and dropped the underscores, but still, **set**index is 
right there. Then I spent a lot of time explaining why setindex is a 
problem.

The problem is, how does the interpreter pass the second positional 
argument without passing something as the first positional argument?

This isn't a problem for subscripting alone. It's a problem for any 
function call:

    def function(first=None, second=None, /):
        print(first, second)

I've explicitly flagged the arguments as "positional only" to avoid 
(non-)solutions that rely on the interpreter knowing the names of the 
parameters at runtime. You can only pass arguments by position, they 
have to be filled left-to-right, and the aim is to successfully pass a 
value for `second` but no value for `first`.

You can't rely on the author of the function to do the argument 
processing like this:


    def function(*args):
        if len(args) == 1:
            second = args[0]
            first = None  # default
        elif len(args) == 2:
            first, second = args
        else:
            raise TypeError('wrong number of arguments')


That's why there are so few functions in the Python ecosystem with a 
signature similar to range:

    range( [start,] end [, step] )

and that's the problem that we solve here by auto-filling some sentinel 
for the index when the subscript is keyword-only.

If the constraints are:

* the right hand side assignment value gets bound to the second 
  positional argument (not counting "self"), as expected;
 
* there are no changes to way the interpreter binds arguments to 
  parameters;

* there is no requirement that all `__setitem__` methods use the 
  same fixed parameter name for the value argument so that it can
  be passed by a standard name (even "self" is just a convention);

* and no runtime introspection by the interpreter to find out what
  the parameter name is (too slow);

then it is hard to see any other solution than to pass a special 
sentinel to setitem to represent the missing index.

If you have any other solutions, or if you have a persuasive argument in 
favour of relaxing one or more of those constraints, I'd love to hear 
it.


-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/EQRPHQL4HKGUPC7DFUIZU5SDCHDL2RBI/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to