[Python-ideas] Unpacking a dict
Steven D'Aprano
steve at pearwood.info
Wed May 25 14:42:08 EDT 2016
On Wed, May 25, 2016 at 01:11:35PM +0000, Michael Selik wrote:
> py> mapping = {"a": 1, "b": 2, "c": 3}
> py> {"a": x, "b": y, "c": z} = mapping
> py> x, y, z
> (1, 2, 3)
I think that is too verbose and visually baffling. I'd rather see
something less general and (in my opinion) more useful:
a, b, c = **mapping
being equivalent to:
a = mapping['a']
b = mapping['b']
c = mapping['c']
It's less general, because you can only use the same names as the keys
in the dict. But most of the time you'll probably want to do that
anyway. Think of (for example) carrying around a "settings" or
"preferences" dict:
prefs = {'width': 80, 'height': 200, 'verbose': False, 'mode': PLAIN,
'name': 'Fnord', 'flags': spam|eggs|cheese, ... }
# plus many more keys:values
There's no need to unpack the entire dict, you can grab only the keys
you need:
width, height = **prefs
# like
width = prefs['width']
height = prefs['height']
Sure, you are forced to use the same variable names as the keys, but
that's what you will probably do most of the time. You're not likely to
write:
foo = prefs['width']
bar = prefs['height']
although you might write:
zip_code = prefs['zip code']
but probably shouldn't. (Just use 'zip_code' as the key.) Another
awkward case is when a key is a keyword:
except_ = prefs['except']
but I expect those cases will be relatively rare, and you can always
manually unpack them the old fashioned way.
Admittedly your syntax would allow those cases, at the cost of a more
verbose statement:
{'width': foo, 'height': bar, 'except': except_, 'zip code': zip_code} = mapping
but I think that's mostly an over-generalisation and too hard to grasp
what is going on.
Naturally the order of the keys doesn't matter:
mode, height, width = **prefs
height, mode, width = **prefs
etc are all the same.
If you twist my arm and force me to come up with syntax for a "change of
variable name", I'd consider:
height, width, zip_code:'zip code', except_:'except' = **mapping
In other words, if the target on the left is a plain name, the unpacking does:
name = mapping['name']
If the target on the left has a colon, it is an identifier followed by
key. The identifier can be any valid reference, including dots and []
subscripts. The key must be a string:
identifier:'key'
which performs:
identifier = mapping['key']
Examples of valid colon targets:
spam:'ham' # like spam = mapping['ham']
spam[1].eggs:'while' # like spam[1].eggs = mapping['while']
etc.
If there's too many targets to comfortably fit on the one line, wrap
them in parentheses to allow line wrapping:
(height, flags, except_:'except',
mymodule.obj.attribute[2].gamma:'gamma', alpha) = **prefs
But the simple case, the case you'll use most of the time, is simple:
height, width, verbose, flags = **prefs
--
Steve
More information about the Python-ideas
mailing list