[Python-ideas] Unpacking a dict
Steven D'Aprano
steve at pearwood.info
Wed May 25 21:56:07 EDT 2016
On Wed, May 25, 2016 at 01:11:35PM +0000, Michael Selik wrote:
> Clojure also supports mapping destructuring. Let's add that to Python!
>
> py> mapping = {"a": 1, "b": 2, "c": 3}
> py> {"a": x, "b": y, "c": z} = mapping
> py> x, y, z
> (1, 2, 3)
> py> {"a": x, "b": y} = mapping
> Traceback:
> ValueError: too many keys to unpack
>
>
> This will be approximately as helpful as iterable unpacking was before PEP
> 3132 (https://www.python.org/dev/peps/pep-3132/).
What is your evidence for this claim? So far I've only seen one real-
world use-case for this, and that single use-case would be well served
by a simpler syntax:
a, b, c = **mapping
which just requires that a, b, c etc are legal names (not general
identifiers). The dict is then unpacked:
a = mapping['a']
etc. Two questions:
(1) For the use-case we've already seen, a "preferences" or "settings"
mapping, do you think users will be prepared to use your syntax?
(2) Do you have any other concrete use cases for this, and if so, what
are they?
For (1), I've taken a prefs dict from a rather small command line script
I've written. This is taken from actual code in use. To unpack the dict
using your syntax, I would have to write:
{'sort': sort, 'reverse': reverse, 'classify': classify,
'showlinks': showlinks, 'showsize': showsize, 'spacer': spacer,
'style': style, 'width': width} = prefs
# next line is optional, but I might not wish to pollute the namespace
del sort, reverse, showlinks, showsize, style
in order to unpack the three keys I actually want. Here's my suggestion:
classify, spacer, width = **prefs
I don't know about you, but there's no way I'd use your suggested
syntax as-shown. I'd rather unpack manually:
classify, spacer, width = [prefs[key] for key in 'classify spacer width'.split()]
or variations of same.
For (2), here's another use-case I can think of. Unpacking **kwargs in
functions/methods.
def spam(self, a, b, **kwargs):
...
I'd like to unpack a small number of keys:values from kwargs, extract
them from the dict, and pass that on to another method:
fnord = kwargs.pop('fnord', 'default')
wibble = kwargs.pop('wibble', 42)
super().spam(a, b, **kwargs)
I don't have a concise syntax for this use-case, and yours won't work
either. There's no way to supply defaults, nor can you list *all* the
keys because you don't know what they will be. (The caller can provide
arbitrary keyword arguments.)
So I don't think this use-case can be handled by either your syntax or
mine for dict unpacking.
I think your syntax is too verbose and repetitive for the simple case.
It does have the advantage that it can deal with keys which aren't
identifiers:
{'while': while_, 'foo bar': foobar} = mapping
but it only looks good in toy examples. In real code, I wouldn't use it,
it would be too painful and repetitive.
If we add syntax to collect all the unused items, your syntax will be a
bit less painful, but still repetitive:
{'classify': classify, 'spacer': spacer, 'width': width, **whocares} = prefs
but that has no advantage over what we already have:
classify, spacer, width = [prefs[key] for key in ('classify', 'spacer', 'width')]
(The two are almost the same length, and equally repetitive.)
As far as changing names, we can already do that, and use arbitrary
references:
myobj.attr['key'][1], while_, foobar = [
mapping[key] for key in ('something', 'while', 'foo bar')]
So I think that the problems your syntax solve are already easy to
solve, and the things which are annoying to solve now, your syntax is
too painful to use.
I'd rather have syntax which is less general but more useful in
practice, than something which solves dict unpacking in its full
generality but a pain to use.
--
Steve
More information about the Python-ideas
mailing list