
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