This proposal is an alternative to Rodrigo's "Keyword arguments
self-assignment" thread.
Rodrigo, please feel free to mine this for useful nuggets in your PEP.
(I don't claim to have invented the syntax -- I think it might have been
Alex Hall?)
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. That means that syntactically it will legal to
mix this between other keyword arguments:
# currently legal
func(z=5, **d1, x=0, y=6, **d2, w=7)
# propose to allow Keyword Unpacking Shortcut as well
func(z=5, **d1, x=0, **{u, v}, y=6, **d2, w=7)
# which would be equivalent to:
func(z=5, **d1, x=0, u=u, v=v, y=6, **d2, w=7)
The order of unpacking should be the same as the order of unpacking
regular dict unpacking:
func(**{a, b, c})
func(**{'a': a, 'b': b, 'c': c})
should unpack the parameters in the same order.
Interpretation
--------------
There are at least two ways to interpret the shortcut:
1. It is like unpacking a dict where only the keys (identifiers) are
given, and the values are implied to be exactly the same as the keys.
2. It is like unpacking a dict formed from a set of identifiers, used
as both the parameter name and evaluated as the argument value.
(This does not imply that the shortcut need actually generate either a
dict or a set. The actual implementation will depend on the compiler.)
Either way, the syntax can be justified:
* Why use double-star `**`? Because it is a form of dict unpacking.
* Why use curly brackets (braces) `{}`? It's a set of identifiers to be
auto-filled with values matching the identifier.
Put them together and you get `**{ ... }` as the syntax.
Examples
--------
The function call:
function(**{meta, invert, dunder, private, ignorecase})
expands to:
function(meta=meta,
invert=invert,
dunder=dunder,
private=private,
ignorecase=ignorecase
)
Because the keyword unpacking shortcut can be used together with other
unpacking operators, it can be (ab)used for quite complex function
calls. `Popen` has one of the most complex signatures in the Python
standard library:
https://docs.python.org/3.8/library/subprocess.html#subprocess.Popen
Here is an example of mixing positional and keyword arguments together
with the proposed keyword unpacking shortcut in a single call to Popen.
subprocess.Popen(
# Positional arguments.
args, bufsize, executable,
*stdfiles,
# Keyword arguments.
shell=True, close_fds=False, cwd=where, creationflags=flags,
env={'MYVAR': 'some value'},
**textinfo,
# Keyword unpacking shortcut.
**{preexec_fn, startupinfo, restore_signals, pass_fds,
universal_newlines, start_new_session}
)
which will expand to:
subprocess.Popen(
# Positional arguments.
args, bufsize, executable,
*stdfiles,
# Keyword arguments.
shell=True, close_fds=False, cwd=where, creationflags=flags,
env={'MYVAR': 'some value'},
**textinfo,
# Keyword unpacking shortcut expands to:
preexec_fn=preexec_fn,
startupinfo=startupinfo,
restore_signals=restore_signals,
pass_fds=pass_fds,
universal_newlines=universal_newlines,
start_new_session=start_new_session
)
Note that plain keyword arguments such as:
cwd=where
have the advantage that the key and value are shown directly in the
function call, but can be excessively verbose when there are many of
them, especially when the argument value duplicates the parameter name,
e.g. `start_new_session=start_new_session` etc.
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.
The keyword unpacking shortcut takes a middle ground. Like explicit
keyword arguments, and unlike the regular `**textinfo` unpacking, the
parameter names are visible in the function call. But it avoids the
repetition and redundancy of repeating the parameter name as in
`start_new_session=start_new_session`.
Backwards compatibility
-----------------------
The syntax is not currently legal so there are no backwards
compatibility concerns.
Possible points of confusion
----------------------------
Sequence unpacking is currently allowed for sets:
func(*{a, b, c}) # Unpacks a, b, c in arbitrary order.
Anyone intending to use the keyword unpacking shortcut but accidentally
writing a single star instead of a double will likely end up with
confusing failures rather than an explicit exception. However, the same
applies to dict unpacking:
func(*{'meta': True', 'invert': False, 'dunder': True})
which will unpack the keys as positional arguments. Experience strongly
suggests this is a rare error in practice, and there is little reason to
expect that keywords unpacking shortcut will be any different. This
could be handled by a linter.
--
Steven
_______________________________________________
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/GXQUERT5ZO2ENRHPOGIVVJLRF6BDZVT4/
Code of Conduct: http://python.org/psf/codeofconduct/