[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 

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:


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']


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


More information about the Python-ideas mailing list