On Apr 17, 2020, at 23:18, Steven D'Aprano <[email protected]> wrote:
>
>
> Keyword Unpacking Shortcut
> --------------------------
>
> Inside function calls, the syntax
>
> **{identifier [, ...]}
>
> expands to a set of `identifier=identifier` argument bindings.
>
> This will be legal anywhere inside a function call that keyword
> unpacking would be legal.
Which means that you can’t just learn ** unpacking as a single consistent thing
that’s usable in multiple contexts with (almost) identical syntax and identical
meaning, you have to learn that it has an additional syntax with a different
meaning in just one specific context, calls, that’s not legal in the others.
Each special case like that makes the language’s syntax a little harder to
internalize, and it’s a good thing that Python has a lot fewer such special
cases than, say, C.
Worse, this exact same syntax is a set display anywhere except in a ** in a
call. Not only is that another special case to learn about the differences
between set and dict displays, it also means that if you naively copy and paste
a subexpression from a call into somewhere else (say, to print the value of
that dict), you don’t get what you wanted, or a syntax error, or even a runtime
error, you get a perfectly valid but very different value.
> On the other hand, plain keyword unpacking:
>
> **textinfo
>
> is terse, but perhaps too terse. Neither the keys nor the values are
> immediately visible. Instead, one must search the rest of the function
> or module for the definition of `textinfo` to learn which parameters are
> being filled in.
You can easily put the dict right before the call, and when you don’t, it’s
usually because there was a good reason.
And there are good reasons. Ideally you shouldn’t have any function calls that
are so hairy that you want to refractor them, but the the existence of
libraries you can’t control that are too huge and unwieldy is the entire
rationale here. Sometimes it’s worth pulling out a group of related parameters
to a “launch_params” or “timeout_and_retry_params” dict, or even to a
“build_launch_params” method, not just for readability but sometimes for
flexibility (e.g., to use it as a cache or stats key, or to give you somewhere
to hook easily in the debugger and swap out the launch_params dict.
> Backwards compatibility
> -----------------------
>
> The syntax is not currently legal so there are no backwards
> compatibility concerns.
The syntax is perfectly legal today. The syntax for ** unpacking in a call
expression takes any legal expression, and a set display is a legal expression.
You can see this by calling compile (or, better, dis.dis) on the string
'spam(**{a, b, c})'.
The semantics will be a guaranteed TypeError at runtime unless you’ve done
something pathological, so almost surely nobody’s deployed any code that
depends on the existing semantics.
But that’s not the same as the syntax not being legal. And, outside of that
trivial backward compatibility nit, this raises a bunch of more serious issues.
Running Python 3.9 code in 3.8 would do the wrong thing, but maybe not wrong
enough to break your program visibly, which could lead to some fun debugging
sessions. That’s not a dealbreaker, but it’s definitely better for new syntax
to raise a syntax error in old versions, if possible.
And of course existing linters, IDEs, etc. will misunderstand the new syntax
(which is worse than failing to parse it) until they’re taught the new special
case.
This also raises an implementation issue. The grammar rule to disambiguate this
will probably either be pretty hairy, or require building a parallel fork of
half the expression tree so you can have an “expression except for set
displays” node. Or there won’t be one, and it’ll be done as a special case
post-parse hack, which Python uses sparingly.
But all of that goes right along with the human confusion. If the same syntax
can mean two different things in different contexts, it’s harder to internalize
a usable approximate version of the grammar. For something important enough,
that may be worth it, but I don’t think the benefits of this proposal reach
that bar.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/I5VC4ONOG4F4KRP3TCQMAT4HCNUZT2O3/
Code of Conduct: http://python.org/psf/codeofconduct/