[Python-ideas] Unpacking a dict

Michael Selik michael.selik at gmail.com
Thu May 26 01:09:02 EDT 2016

On Wed, May 25, 2016 at 9:56 PM Steven D'Aprano <steve at pearwood.info> wrote:

> > This will be approximately as helpful as iterable unpacking
> What is your evidence for this claim?

[For extracting from a large dictionary] I'd rather unpack manually:
>     classify, spacer, width = (prefs[k] for k in ('classify', 'spacer',
> 'width'))

I agree that comprehensions plus tuple unpacking handle many possible use
cases for dict unpacking.

There are many marginally-better situations that would just drum up endless
back-and-forth about aesthetics. So let's look for a situation where dict
unpacking handles well what current syntax struggles with.

An example of schema validation plus binding in the current syntax:
    py> mapping = {'a': 1, 'b': 2}
    py> schema = ('a', 'b')
    py> unexpected = mapping.keys() - set(schema)
    py> if unexpected:
    ...     raise ValueError('unexpected keys %r' % unexpected)
    py> x, y = (mapping[key] for key in schema)

With sets and comprehensions, that's very nice. More pleasant than most if
not all other mainstream languages. Yet dict unpacking can be just a bit
better -- more declarative, more say-what-you-mean, less thinking about
algorithms. Fewer steps, too, but that's not very important.

The proposed syntax, examples of missing and excess keys.
    py> mapping = {'a': 1, 'b': 2, 'c': 3}
    py> {'a': x, 'b': y, 'c': z, 'd': s, 'e': t} = mapping
    ValueError: missing 2 required keys 'd' and 'e'
    py> {'a': x, 'b': y} = mapping
    ValueError: got an unexpected key 'c'

If you tell me that's not enough better to justify changing the language, I
won't argue very much. I agree that example, if that's the only use case,
would need to be backed up by extensive review of code of major projects to
see how much improvement it'd really provide.

Unpacking really starts to shine when doing nested/recursive destructuring.

My proposed syntax:
    py> d = {'a': 1,
    ...      'b': {'c': 2, 'd': 3}}
    py> {'a': x, 'b': {'c': y, 'd': y}} = d
    py> x, y, z
    1, 2, 3

In current syntax, even simply specifying the schema is troublesome if the
order of keys is to be preserved for binding to the desired names.

    >>> mapping = {'a': 1, 'b': {'c': 2, 'd': 3}}
    >>> schema = OrderedDict([('a', None),
    ...                       ('b', OrderedDict([('c', None), ('d',
    >>> x, y, z = ...

I tried writing out a couple comprehensions for the binding, but they were
*ugly*. The traversal of nested mapping for validation, flattening and
assignment needs a recursive function call or you'll end up with a disaster
of nested loops.

And lastly, if you have your eye on the prize (pattern matching) then
establishing a full-featured dict unpacking is a big step in the right
direction. I may not have stated it when I started this thread, but the
initial motivation for dict unpacking was the trouble we were having in our
discussion of pattern matching. I wanted to break that big problem apart
into smaller problems.

A possible (not proposed) syntax for dict and tuple pattern matching:
    py> {'a': x, 'b': 0} = {'a': 1, 'b': 2}
    ValueError: key 'b' does not match value 0
    py> (a, 0) = (1, 2)
    ValueError: index 1 does not match value 0

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.)

You're right, I haven't thought about defaults. Hmm. Tuple unpacking
doesn't support defaults either, so I guess I'll let this one go as not
appropriate for dict unpacking.

I think your syntax is too verbose and repetitive for the simple case.

It's no more repetitive than str.format with keyword arguments :-)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160526/697f00d6/attachment-0001.html>

More information about the Python-ideas mailing list