On Thu, Aug 27, 2020, 8:34 AM Steven D'Aprano <steve@pearwood.info> wrote:

> 3. Removes the need to write calls to the same supporting function in the
> item dunders. They are called automatically.

I don't understand that sentence.


[...]
> My current attempt is this:
>
> def __subscript__(self, *args, **kwargs) -> Tuple [Tuple[Any], Dict[str,
> Any]]:
>     return t

NameError: name 't' is not defined


> Which, for a getitem dunder call, `t` becomes:
>
> obj.__getitem__(*t[0], **t[1])

What does this mean? You assign

    t = obj.__getitem__(*t[0], **t[1])

and then return t?


--
Steve

Sorry, I need to stop coding in shorthand.

Here is a more fleshed out illustration of what I am imagining would happen. I welcome suggestions for better ways to do pass the arguments on to the item dunders-- the proposal remains the basic idea nugget Jonathan Fine presented:

1. A dunder that handles the args and kwargs sent into the subscript operator in whatever way the programmer wants (the default way, when no such dunder is provided, being current python behavior).
2. MAGIC --- a suggestion for what the magic could be, below.
3. The appropriate item dunder is called with the parsed parameters

First, we have an example class with a new __subscript__ dunder. Remember, the __subscript__ dunder can have any signature you want, and do anything to the arguments inside that you want, it just needs to return something matching the type hint below when done:

class Q:
    def __subscript__(self, a, b, c, *, x, y, z, **kwargs) -> Sequence[Sequence[Any], Mapping[str, Any]]:
        kwargs.update(x=x, y=y, z=z)
        return (a,b,c), kwargs
    def __getitem__(self, *args, **kwargs): ...
    def __setitem__(self, __value, *args, **kwargs): ...
    def __delitem__(self, *args, **kwargs): ...

Here is what I imagine happens at the python level (but I am writing it in python):

MISSING = object()

def cpython_subscript_operator_function(obj, *args, __item_dunder_name, __value=MISSING, **kwargs):
    if __item_dunder_name == "__setitem__" and __value is not MISSING:
        args = (__value, *args)
    obj_class = type(obj)
    subscript_dunder =  getattr(obj_class, "__subscript__", None)
    if  subscript_dunder:
        args, kwargs =  subscript_dunder(obj, *args, **kwargs)
    return getattr(obj_class,  __item_dunder_name)(obj, *args, **kwargs)

Now, when I write this python code:

>>> q=Q()
>>> q[1, x=4, y=5, z=6, foo='bar', b=2, c=3]

That code calls this cpython pseudo code:

cpython_subscript_operator_function(q, 1, __item_dunder_name="__getitem__", x=4, y=5, z=6, foo='bar', b=2, c=3)

And if I write set item code, it is like this:

>>> q[1, 2, 3, x=4, y=5, z=6, foo='bar'] = "baz"

cpython level call looks like:

cpython_subscript_operator_function(q, 1, __item_dunder_name="__getitem__", __value="baz", x=4, y=5, z=6, foo='bar', b=2, c=3)