[Python-ideas] Dictionary destructing and unpacking.
Steven D'Aprano
steve at pearwood.info
Wed Jun 7 21:18:06 EDT 2017
On Wed, Jun 07, 2017 at 06:14:08PM +0000, Nick Humrich wrote:
> It would be cool to have a syntax that would unpack the dictionary to
> values based on the names of the variables. Something perhaps like:
>
> a, b, c = **mydict
This was discussed (briefly, to very little interest) in March/April
2008:
https://mail.python.org/pipermail/python-ideas/2008-March/001511.html
https://mail.python.org/pipermail/python-ideas/2008-April/001513.html
and then again in 2016, when it spawned a very large thread starting
here:
https://mail.python.org/pipermail/python-ideas/2016-May/040430.html
I know there's a lot of messages, but I STRONGLY encourage anyone,
whether you are for or against this idea, to read the previous
discussion before continuing it here.
Guido was luke-warm about the **mapping syntax:
https://mail.python.org/pipermail/python-ideas/2016-May/040466.html
Nathan Schneider proposed making dict.values() take optional key names:
https://mail.python.org/pipermail/python-ideas/2016-May/040517.html
Guido suggested that this should be a different method:
https://mail.python.org/pipermail/python-ideas/2016-May/040518.html
My recollection is that the discussion evertually petered out with a
more-or-less consensus that having a dict method (perhaps "getvalues"?)
plus regular item unpacking is sufficient for the common use-case of
unpacking a subset of keys:
prefs = {'width': 80, 'height': 200, 'verbose': False, 'mode': PLAIN,
'name': 'Fnord', 'flags': spam|eggs|cheese, ... }
# dict includes many more items
width, height, size = prefs.getvalues(
'width', 'height', 'papersize',
)
This trivially supports the cases where keys are not strings or valid
identifiers:
class_, spam, eggs = mapping.getvalues('class', 42, '~')
It easily supports assignment targets which aren't simple variable
names:
obj.attribute[index], spam().attr = mapping.getvalues('foo', 'bar')
An optional (defaults to False) "pop" keyword argument supports
extracting and removing values from the dict in one call, which is
commonly needed inside __init__ methods with **kwargs:
class K(parent):
def __init__(self, a, b, c, **kwargs):
self.spam = kwargs.pop('spam')
self.eggs = kwargs.pop('eggs')
self.cheese = kwargs.pop('cheese')
super().__init__(a, b, c, **kwargs)
becomes:
self.spam, self.eggs, self.cheese = kwargs.getvalues(
'spam eggs cheese'.split(), pop=True
)
I don't recall this being proposed at the time, but we could support
keyword arguments for missing or default values:
DEFAULTS = {'height': 100, 'width': 50}
prefs = get_prefs() # returns a dict
height, width, size = prefs.getvalues(
'height', 'width', 'papersize',
defaults=DEFAULTS,
missing=None
)
A basic implementation might be:
# Untested.
def getvalues(self, *keys, pop=False, defaults=None, missing=SENTINEL):
values = []
for key in keys:
try:
x = self[key]
except KeyError:
if defaults is not None:
x = defaults.get(key, SENTINEL)
if x is SENTINEL:
x = missing
if x is SENTINEL:
raise KeyError('missing key %r' % key)
if pop:
del self[key]
values.append(x)
return tuple(values)
It's a bit repetitive for the common case where keys are the same as the
assignment targets, but that's a hard problem to solve, and besides,
"explicit is better than implicit".
It also doesn't really work well for the case where you want to blindly create new assignment targets for
*every* key, but:
- my recollection is that nobody really came up with a convincing
use-case for this (apologies if I missed any);
- and if you really need this, you can do:
locals().update(mapping)
inside a class body or at the top-level of the module (but not inside a
function).
Please, let's save a lot of discussion here and now, and just read the
2016 thread: it is extremely comprehensive.
--
Steve
More information about the Python-ideas
mailing list