[Python-ideas] Yet More Unpacking Generalizations (or, Dictionary Literals as lvalues)
Scott Sanderson
scott.b.sanderson90 at gmail.com
Wed Aug 12 15:57:03 CEST 2015
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.
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.
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)
Nearly all the arguments in favor of tuple/list unpacking also apply to
this construct. In particular:
1. It makes the code more self-documenting, in that the left side of the
expression looks more like the expected output of the right side.
2. The construct can be implemented more efficiently by the interpreter by
using a dictionary analog of the UNPACK_SEQUENCE opcode (e.g. UNPACK_MAP).
An interesting question that falls out of this idea is whether/how we
should handle nested structures. I'd expect the rule to be that something
like:
{'toplevel': {'key1': key1, 'key2': key2}} = value
would desugar into something equivalent to:
TEMP = value['toplevel']
key1 = TEMP['key1']
key2 = TEMP['key2']
del TEMP
while something like
{'toplevel': (x, y)} = value
would desugar into something like:
(x, y) = value['toplevel']
At the bytecode level, I'd expect this to be implemented with a new
instruction, analogous to the current UNPACK_SEQUENCE, which would pop N
keys and a map from the stack, and push map[key] onto the stack for each
popped key. We'd then recurse through the values left on the stack,
storing them as we would store the sub-lvalues if they were in a
standard assignment. Thus the code for something like:
{'name': name, 'tuple': (x, y), 'dict': {'subkey': subvalue}} = values
would translate into the following "pseudo-bytecode":
LOAD_NAME 'values' # Push rvalue onto the stack.
LOAD_CONST 'dict' # Push top-level keys onto the stack.
LOAD_CONST 'tuple'
LOAD_CONST 'name'
UNPACK_MAP 3 # Unpack keys. Pops values and all keys from the stack.
# TOS = values['name']
# TOS1 = values['tuple']
# TOS2 = values['dict']
STORE_FAST name # Terminal names are simply stored.
UNPACK_SEQUENCE 2 # Push the two entries in values['tuple'] onto the
stack.
# TOS = values['tuple'][0]
# TOS1 = values['tuple'][1]
# TOS2 = values['dict']
STORE_FAST x
STORE_FAST y
LOAD_CONST 'subkey' # TOS = 'subkey'
# TOS1 = values['dict']
UNPACK_MAP 1 # TOS = values['dict']['subkey']
STORE_FAST subvalue
I'd be curious to hear others' thoughts on whether this seems like a
reasonable idea. One open question is whether non-literals should be
allowed as keys in dictionaries (the above still works as expected if the
keys are allowed to be names or expressions; the LOAD_CONSTs would turn
into whatever expression or LOAD_* is necessary to put the necessary value
on the stack). Another question is if/how we should handle extra keys in
right-hand side of the assignment (my guess is that we shouldn't do
anything special with that case).
-Scott
P.S. I attempted to post this last night, but it seems to have not gone
through. Apologies for the double post if I'm mistaken about that.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150812/b245a786/attachment-0001.html>
More information about the Python-ideas
mailing list