I hope this post is not a duplicate of any other somewhere or of some PEP. At least I didn’t find any on this forum regarding the hereby matter.
The idea
At the moment, tuples are very useful for holding many values and unpack it as function parameters or to assign many variables at a time:
def myfunction(these, are, ordered, values): ...
args = ('these', 'are', 'ordered', 'values')
myfunction(*args) # unpacking to arguments
a, b, c, *_ = args # unpacking to variables
All this is allowed by the iterator protocol.
We can also unpack as aguments using the mapping protocol
def myfunction(these, values, are, unordered): ...
kwargs = {'these':1, 'are':2, 'unordered':3, 'values':4}
myfunction(**kwargs) # unpacking to values named like the target arguments
I think it could be great to have a new syntax for unpacking to variables, but using the mapping protocol like for functions. So it would provide a concise way of getting specific values from dictionnaries.
# this could be very cool !
kwargs = {'these':1, 'are':2, 'unordered':3, 'values':4}
these, values = **kwargs # unpacking the values named like the target variables
assert (these, values) == 1, 4
Current workarounds
I know mainly two ways of doing similar things, but none are as satisfying as the latter dedicated syntax.
-
working with globals (uh…)
globals().update(kwargs)
It only works in a module scope, and it is not a good design. Also it’s unpacking all the variables from kwargs so it is not as flexible as a proper unpacking.
Doing the same in a local scope is not possible since for performance reasons, function scopes do not store variables in a dictionnary nor any kind of user-accessible mapping. -
using
operator.itemgetter
these, values = itemgetter('these', 'values')(kwargs)
Well it works but needs repetition of the desired variables, which is painful if we have a certain number of variables to extract. Also it’s creating a temporary object that is immediately called then destroyed: this is a bit slow only for variables assignment.
Alternatives
These are just ideas, without any consideration of feasibility nor interest.
-
working with locals, the same as with globals
locals().update(kwargs)
this would require to create a new mapping type dedicated to call frames; Allowing to access and assign the scope variables in the scope’s call frame using the mapping protocol. So it would be a dictionnary-like object, with no insertion abillity
-
I don’t know what else
Motivations
-
this ease the use of dictionnaries as return values instead of tuples
-
this makes extraction of values from dictionnaries much easier and readable (for config, set of vars, provided environments, etc)
-
this makes more similar the use of functions returning tuples and functions returning dictionnaries.
complex functions returning a bunch of values often hesitate between tuples (for small set of values) and dictionnaries (for bigger set, or when only few values are used at a time by the caller, but computed anyway). This is for instance often the case in the machine-leanrning libraries I’ve been working with, and this is the case of many scipy functions too. -
this plays well with the already existing antagonism between mapping and iterator
-
this brings an elegant, efficient, and hygienic answer to the iconic question of updating
locals()