
On Apr 21, 2020, at 01:27, M.-A. Lemburg <mal@egenix.com> wrote:
On 21.04.2020 04:25, Andrew Barnert via Python-ideas wrote:
On Apr 20, 2020, at 16:24, M.-A. Lemburg <mal@egenix.com> wrote:
On 20.04.2020 19:43, Andrew Barnert via Python-ideas wrote:
On Apr 20, 2020, at 01:06, M.-A. Lemburg <mal@egenix.com> wrote:
The current version already strikes me as way too complex. It's by far the most complex piece of grammar we have in Python:
funcdef: 'def' NAME parameters ['->' test] ':' [TYPE_COMMENT] func_body_suite
But nobody’s proposing changing the function definition syntax here, only the function call syntax. Which is a lot less hairy. It is still somewhat hairy, but nowhere near as bad, so this argument doesn’t really apply.
True, I quoted the wrong part of the grammar for the argument, sorry. I meant this part:
https://docs.python.org/3/reference/expressions.html#calls
which is simpler, but not really much, since the devil is in the details.
Let’s just take one of the variant proposals under discussion here, adding ::identifier to dict displays. This makes no change to the call grammar, or to any of the call-related bits, or any other horribly complicated piece of grammar. It just changes key_datum (a nonterminal referenced only in dict_display) from this:
expression ":" expression | “**” or_expr
… to this:
expression ":" expression | “::” identifier | “**” or_expr
That’s about as simple as any syntax change ever gets.
Which is still not nothing. But you’re absolutely right that a big and messy change to function definition grammar would have a higher bar to clear than most syntax proposals—and for the exact same reason, a small and local change to dict display datum grammar has a lower bar than most syntax proposals.
I think the real issue you would like to resolve is how to get at the variable names used for calling a function, essentially pass-by-reference (in the Python sense, where variable names are references to objects, not pointers as in C) rather than pass-by-value, as is the default for Python functions.
No, nobody’s asking for that either. It wouldn’t directly solve most of the examples in this thread, or even indirectly make them easier to solve. The problem in most cases is that they have to call a function that they can’t change with a big mess of parameters. Any change to help the callee side doesn’t do any good, because the callee is the thing they can’t change. The fix needs to be on the caller side alone. This also wouldn’t give you useful pass-by-reference in the usual sense of “I want to let the callee rebind the variables I pass in”, because a name isn’t a reference in Python without the namespace to look it up in. Even if the callee knew the name the caller used for one of its parameters, how would it know whether that name was a local or a cell or a global? If it’s a local, how would it get at the caller’s local environment without frame hacking? (As people have demonstrated on this thread, frame hacking on its own is enough, without any new changes.) Even if it could get that local environment, how could it rebind the variable when you can’t mutate locals dicts? Also, most arguments in Python do not have names, because arguments are arbitrary expressions. Of course the same thing is true in, say, C++, but that’s fine in C++, because lvalue expressions have perfectly good lvalues even if they don’t have good names. You can pass p->vec[idx+1].spam to a function that wants an int&, and it can modify its parameter and you’ll see the change on your side. How could your proposal handle even the simplest case of passing lst[0]? Even if it could work as advertised, it’s hugely overkill for this problem. A full-blown macro system would let people solve this problem, and half the other things people propose for Python, but that doesn’t mean that half the proposals on this list are requests for a full-blown macro system, or that it’s the right answer for them.
The f-string logic addresses a similar need.
Similar, yes, but the f-string logic (a) runs in the caller’s scope and (b) evaluates code that’s textually part of the caller.
With a way to get at the variable names used for calling a function from inside a function, you could then write a dict constructor which gives you the subset of vars() you are looking for.
Most of the use cases involve “I can’t change the callee, so I need to give it the ugly mess of keyword args that it wants”. So any proposal to allow changing callees is already on the wrong track. But even if this were acceptable, how would it help anything? Take any of the examples from this thread, ignore the “I can’t change the function I’m calling part”, and show how you’d solve it if the callee had the caller’s names for its parameters. Also, “you can solve this with ugly introspection code” does not always mean we don’t need a better solution. In fact, you can solve this today, from the caller side, with something like this (not in front of a computer right now, so probably most of the details are wrong, but hopefully you can get the idea): sig = inspect.signature(spam) spamargs = {k: v for (k, v) in vars().items() if k in sig.kwargs} spam(**spamargs) … but it should be obvious why nobody writes that code rather than: spam(eggs=eggs, cheese=cheese, ham=ham) They want a way to make things less verbose and less error prone, and I just offered them a way to make things more verbose and more error prone, and I doubt any of them will take it. You’re offering them a way to rewrite the function they can’t rewrite so they can have another way to make things more verbose and more error prone and also more unsafe and nonportable, and I doubt that will be any more helpful.
Rather than using new syntax, a helper in the compiler would do the trick, though, similar to what e.g. @classmethod does.
@classmethod doesn’t have any help from the compiler. It’s a trivial use of the same __get__ protocol, which you could write yourself. And it’s not clear what kind of compiler helper you could use for your idea. Remember that when compiling a call, the compiler has no idea what the callable is; it can’t know, because that’s completely dynamic. So anything you want to magically sweep up and package from the caller side has to be passed in to every call. You surely wouldn’t want to pass along the name of every argument in every call? That would be a huge backward compatibility break and a huge performance cost, and also impossible because most arguments don’t have names.