On Wed, Aug 26, 2020 at 10:34 PM Steven D'Aprano <st...@pearwood.info>
wrote:

> On Wed, Aug 26, 2020 at 12:32:56PM -0400, Ricky Teachey wrote:
> > It creates a language supported way for the creator of the class to
> decide
> > how to interpret the contents inside a subscript operation.
>
> We already have that: `__getitem__`.
>

Actually no, we have *THREE* dunders: get, set, and del -item. And
they accept a single argument containing a single object representing a
single key or index. And if I had to make a prediction, then probably
pretty soon they'll accept kwargs, too.

And these are great as-is when the way you want to use them is in total
agreement with the way they were envisioned to be used: for single argument
keys/indexes.

But any use case that deviates from that vision requires effort spread
across three dunder methods to set it up right. And it requires writing
code that breaks up the positional arguments in just the right way; code
that you pretty much never have to write when using the function paradigm
(rather than the item dunder paradigm), because the language does it all
for us:

def f(pos1, pos2, *args, kwd1, kwd2=None, **kwargs): ...

I don't have to write code in the function body of `f` breaking the
arguments up the way I intend to use them, and errors are raised when
required arguments are missing, etc etc. This is a set of fantastic
language features.

But the positional part of it is missing from subscripting, from
__getitem__, __setitem__, and __delitem__ and you have to break up your
positionals in three different dunder methods.

If you are a person who wants to use the subscripting operator from a more
function-like paradigm, this is kind of a lot of work and extra code to
maintain-- code that simply would not be needed at all if there was a
single dunder responsible for accepting the key/index "stuff" and then
sending it off-- somehow-- into the item dunders to be used.

How is it sent? I don't know yet. Thinking about it.


> > It greatly
> > alleviates (though not perfectly-- see the end of this message) the
> > incongruity between how the indexing operator behaves and function calls
> > behave. As explained by Jonathan Fine, it adds flexibility
>
> What flexibility is added that `__getitem__` doesn't already provide?
>

I explained above: a single point of subscript argument acceptance that
provides all the language features of function signatures.

> and smoothes out
> > the differences the different paradigms of subscript operations:
> sequences,
> > and mappings.
>
> Won't sequences and mappings still call the same dunder method?
>

Yes, but again, all the existing power the language has for calling
functions and passing them to the function signature can be brought to the
table for all three subscript operators using a single new dunder. This
smoothes things out, because if you don't want to use the default python
treatment of positional subscript arguments as a single key or index
object, it makes it much easier to do things another way.

> And it opens up opportunities for other paradigms to be
> > created, the most prominent example of which is type-hint creation
> > shortcuts, like:
> >
> > Vector = Dict[i=float, j=float]
>
> That's not a paradigm. That's just an application of the feature.
> *Type-hints* was a major change to the Python's language paradigm. This
> is just a (potential) nice syntax that allows a concise but readable
> type-hint.
>
> https://en.wikipedia.org/wiki/Paradigm


I'm not going to respond to this other than to say that I think I was clear
and providing a link to a dictionary entry for "paradigm" sort of feels
like picking on my choice of words rather than actually responding to me
with candor. If so, that doesn't seem like a very kind conversation choice.
If I am correct, I'd like to ask that in the future, please choose to be
more kind when you respond to me? Thank you.

On the other hand if I am wrong and it wasn't clear what I am saying, or if
you were just being humorous, I very much apologize for being
oversensitive. :)

You're suggesting that if subscript keywords were permitted, rather than
> having the keywords passed directly to `__getitem__` where they are
> wanted, the interpreter should pass them to another method and then that
> method could pass them to `__getitem__`.
>

The only thing Jonathan and I are proposing at this time is a __dunder__
that gets passed the contents of the subscript operator and does the
handling of the contents they get passed along:

1.  New __dunder__ that gets the contents
2. ???
3. Item dunders called the way the user wants them called.

The details of 2-- how that function "passes on" that information to the
item dunders-- I am currently unsure about.

ONE way might be to have the new method call the existing dunders. But
that's not my preference-- I would like to find another way. I do have
another idea, and I presented it in a previous response. Here it is again:

def __key_or_index_translator__(self, pos1, pos2, *, x) ->
Tuple[Tuple[Any], Dict[str, Any]]:
    """I only allow subscripting like this:

        >>> obj[1, x=2]
    """
    return (pos1, pos2), dict(x=x)

The returned two-tuple, `t`, in turn gets unpacked like for each of the
existing item dunders:

obj.__getitem__(*t[0], **t[1])
obj.__setitem__(value, *t[0], **t[1])
obj.__delitem__(*t[0], **t[1])

Or we could just pass them directly into `__getitem__`, as requested.
>
> How does the interpreter know to call yay_kwargs rather than some other
> method?
>

The yay_kwargs would be assigned to the new dunder attribute:

class C:
    __new_dunder__ = yay_kwargs

However, again, I am not really proposing that the dunder actually calls
__get_item__, etc. That is one way to do it, but I don't like it much. I
think I like the way above a lot better. But I am open to other suggestions.


> I feel like I'm trying to nail jelly to a wall, trying to understand
> your proposal. Every time I ask a question, it seems to twist and mutate
> and become something else.
>

That's because I have been exploring multiple ideas at the same time. And
this particular proposal is really the nugget of an idea currently, the
details of which are changing as part of an ongoing conversation with input
from other people.

Parenthetically: my comments above about what I felt like was a lack of
kindness notwithstanding, I very much appreciate your direct engagement on
this topic with me. You've pointed several things out in the process that
have informed my thinking. And you've been really patient considering that
you've made it clear you'd prefer not to have anymore additional proposals
derailing things. :) So really, thank you.


> And how does this relate to Jonathan's idea of "signature dependent
> semantics" which you were singing the praises of, but seems to have
> nothing to do with what you are describing now.
>

Well I don't want Jonthan Fine blamed for that one: it was actually my
idea. I floated it in the other thread, the title of which was:

"Changing item dunder method signatures to utilize positional arguments
(open thread)"

The purpose of that thread is to explore options for how we might find a
backwards compatible way to allow subscripting to be used from a function
paradigm, if desired by the programmer. The sigdepsum approach was just one
such idea, and it doesn't relate to this. I have been presenting several
ideas I realize, but it's the ideas list after all.

I'm going to ignore the business about `__subscript__` because you seem
> to have backed away from it in a later email, except for one relatively
> minor point. You proposed this:
>
> > No  CODE            CALLS
> > 1.    q[1]                 q.__subscript__("__getitem__", 1)
> > 2.    q[1,]                q.__subscript__("__getitem__", 1)
> > 3.    q[(1,)]              q.__subscript__("__getitem__", 1)
>
> But that's not what subscripting does now.


Yes, backed away from calling __getitem__ inside of __subscript__.

And yes: I know that's not what it does. That's what it will do if we
created a new dunder that uses the function paradigm for accepting the
arguments so they can be passed along to the existing dunders.

If you use `q[1,]` the
> getitem method gets the tuple (1,), not the int 1. So if you're serious
> about that, it's a breaking change.
>

It isn't breaking. Because the default key or index translation function,
or __subscript__ function-- the one that implicitly exists today-- won't
change. It does this, as you know:

CODE                         CALLS
q[1,]                             q.__getitem__((1,))
q[1,] = None                q.__setitem__((1,), None)
del q[1,]                       q.__delitem__((1,))

The proposal is that if a different translation function isn't provided,
things just get passed to the existing 3 dunders as they do today. Only in
the case that a new dunder-- something like __subscript__-- gets called
does to change to this:

CODE                         FIRST CALLED                         LATER
CALLED
q[1,]                             q.__subscript__(1)
q.__getitem__ in whatever way __subscript__ directs
q[1,] = None                q.__subscript__(None, 1)
  q.__setitem__ in whatever way __subscript__ directs
del q[1,]                       q.__subscript__(1)
 q.__delitem__ in whatever way __subscript__ directs


---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
_______________________________________________
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/HYJOLTTNK43GDS6TR7T5OZWOWMPXREOP/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to