I think that anything that is only needed "occasionally" doesn't have a
strong claim to deserve syntax.
I suppose I might have worded this more strongly. I meant "occasionally" to be interpreted as "often enough that I've been irked by not having a cleaner way to express this construct". I do appreciate the fact that syntax extensions have a real cost, and that they should be reserved for cases where the additional clarity and/or economy of expression outweighs the cost of implementation, maintenance, and (perhaps most importantly) teaching the language to others.
I think the sequence unpacking version above reads much better than
this hypothetical dict unpacking version:
{'foo': foo, 'bar': bar, 'baz': baz, 'spam': spam,
'eggs': eggs, 'cheese': cheese} = json.loads(json_dict)
As with many things in Python, I think that how you format this expression makes a big difference. I'd write it like this:
{
'foo': foo,
'bar': bar,
'baz': baz,
'spam': spam,
'eggs': eggs,
'cheese': cheese,
} = json.loads(json_dict)
I prefer this to your example of unpacking from a list comprehension because I think it does a better job of expressing to a reader the expected structure of the input data. It's also much easier to modify this to extract nested values, ala:
{
'foo': foo,
'bar': bar,
'baz': (baz_x, baz_y),
'spam': spam,
'eggs': eggs,
'cheese': cheese,
} = json.loads(json_dict)
However, if you wanted to think outside the box, it's a pity that locals() is not writable. If it were, we could do:
locals().update(json.loads(json_dict))
locals() is already writable in certain contexts, most notably in class bodies. This works fine, for example:
In [1]: class Foo(object):
...: locals().update({'foo': lambda self: 3})
...:
In [2]: Foo().foo()
Out[2]: 3
locals() is not writable, as you point out, in function calls. However, I'm not sure that having a mutable locals is a good solution to this problem. As mentioned in the original post, I most often want to do this in contexts where I'm unpacking serialized data, in which case it's probably not a great idea to have that data trample your namespace with no restrictions.
More practical, and in the spirit of tuple unpacking:
spam, eggs, cheese = **expression
I like how concise this syntax is. I'd be sad that it doesn't allow unpacking of nested expressions, though I think we disagree on whether that's actually an issue.
A more substantial objection might be that this could only work on mapping objects with strings for keys.
- Scott
On Wednesday, August 12, 2015 at 12:38:47 PM UTC-4, Steven D'Aprano wrote:On Wed, Aug 12, 2015 at 09:57:03AM -0400, Scott Sanderson wrote:
> Hi All,
>
> Occasionally I find myself wanting to unpack the values of a dictionary
> into local variables of a function. This most often occurs when
> marshalling values to/from some serialization format.
I think that anything that is only needed "occasionally" doesn't have a
strong claim to deserve syntax.
> For example:
>
> def do_stuff_from_json(json_dict):
> actual_dict = json.loads(json_dict)
> foo = actual_dict['foo']
> bar = actual_dict['bar']
> # Do stuff with foo and bar.
Seems reasonable and not too much of a burden to me. If I needed a lot
of keys, I'd do:
# sequence unpacking version
spam, eggs, cheese, foo, bar, baz = [actual_dict[key] for
key in "spam eggs cheese foo bar baz".split()]
> In the same spirit as allowing argument unpacking into tuples or lists,
> what I'd really like to be able write is something like:
>
> def do_stuff_from_json(json_dict):
> # Assigns variables in the **values** of the lefthand side by doing lookups
> # of the corresponding keys in the result of the righthand side expression.
> {'foo': foo, 'bar': bar} = json.loads(json_dict)
I think the sequence unpacking version above reads much better than
this hypothetical dict unpacking version:
{'foo': foo, 'bar': bar, 'baz': baz, 'spam': spam,
'eggs': eggs, 'cheese': cheese} = json.loads(json_dict)
Both are roughly as verbose, both have a little duplication, but the
sequence unpacking version requires far fewer quotation marks and other
punctuation. I also think it's much more readable, and of course the big
advantage of it is that it works right now, you don't have to wait two
or three years to start using it in production.
If there is a downside to the sequence unpacking version, it is that it
requires a temporary variable actual_dict, but that's not a real
problem.
I don't think dict unpacking is needed when you have only two or three
variables, and I don't think your suggested syntax is readable when you
have many variables. So I would be -1 on this suggestion.
However, if you wanted to think outside the box, it's a pity that
locals() is not writable. If it were, we could do:
locals().update(json.loads(json_dict))
although of course that might update too many local names. So, just
throwing it out there for discussion:
- Make locals() writable. If the compiler detects that locals() may be
written to, that will have to disable the fast local variable access
for that specific function.
More practical, and in the spirit of tuple unpacking:
spam, eggs, cheese = **expression
being equivalent to:
_tmp = expression
spam = _tmp['spam']
eggs = _tmp['eggs']
cheese = _tmp['cheese']
del _tmp
except that _tmp is never actually created/deleted.
This is easier to write and simpler to read, and doesn't allow nested
unpacking. (I consider that last point to be a positive feature, not a
lack.)
--
Steve
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/