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/