
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