On 2020-07-18 10:04, Steven D'Aprano wrote:
On Sat, Jul 18, 2020 at 12:18:38AM -0400, Ricky Teachey wrote:
On Fri, Jul 17, 2020 at 7:21 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, Jul 17, 2020 at 11:11:17AM -0400, Ricky Teachey wrote:
...
For backwards-compatibility, there will only ever be a single positional argument passed into the method. That's because comma-separated values in a subscript are already passed as a tuple:
# this calls __getitem__ with a single tuple argument obj[a,b:c,d] ==> (1, slice(2, 3), 4)
So that's not going to change (at least not without a long and painful deprecation process). But adding support for keyword arguments requires no changes to any existing class or a new builtin "key object" type.
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?
Break a million existing scripts, applications and libraries that rely on `__getitem__` only receiving a single tuple argument when passed comma-separated values? I don't think the core devs will accept that, I think the numpy devs will object strongly, and I'm pretty sure that the Steering Council will say no.
But if you disagree, then feel free to start writing a PEP.
The fact that multiple comma-separated subscripts are passed to the method as a single tuple argument is a historical fact we (almost certainly) cannot change now. But that is orthogonal to how we choose to proceed with keyword arguments. We aren't obliged to repeat the same design.
We have a few choices:
(1) There is a minor inconsistency between subscripts and function calls, so let's just forget all about the whole idea. If we cannot agree on a decision, this is the default. (Status quo wins a stalement.)
(2) Let the Perfect be the enemy of the Good. No compromises! Insist on breaking the entire Python ecosystem for the sake of fixing this minor inconsistency between subscripting and function calls.
(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)
(4) Or keep the subscript processing as-is, for backwards-compatibility, but pass keyword arguments as normal for functions.
Both (3) and (4) would get the job done, but (3) requires everyone who needs keyword arguments to parse the tuple and/or key object by hand to extract them. Having done something similiar in the past (emulating keyword-only arguments in Python 2), I can tell you this is painful.
With (4), the interpreter automatically matches up passed keyword arguments to my `__getitem__` parameters, filling in defaults if needed, and I can concentrate on using the arguments, not parsing them.
[snip] I haven't followed this thread for a while, but, to me, it seems that the simplest option would be to pass the keyword arguments as a dict: obj[a, b:c, x=1] does obj.__getitem__((a, slice(b, c)), dict(x=1)) If there are no keyword arguments, then there's no dict. This does mean that __getitem__ could be called with 1 or 2 arguments and __setitem__ could be called with 2 or 3 arguments, so it would be advisable to make the additional argument optional: def __getitem__(self, args, kwargs=None): Additionally, __setitem__ would look a little odd: def __setitem__(self, args, value, kwargs=None): It would raise a TypeError if there were keyword arguments, but __getitem__, etc, didn't accept any.