8

I have some code that looks something like this:

d = {'foo': True, 'bar': 42, 'baz': '!'}

a = {'foo': d['foo'], 'bar': d['bar']}
b = {'foo': d['foo'], 'baz': d['baz']}
c = {'bar': d['bar'], 'baz': d['baz']}

Surely there's a better way to express this. I actually read the docs in the hope that a dictionary's copy method accepts keys to be included in the new dictionary:

# I'd hoped that something like this would work...
a = d.copy('foo', 'bar')
b = d.copy('foo', 'baz')
c = d.copy('bar', 'baz')

I could write a function for this purpose:

copydict = lambda dct, *keys: {key: dct[key] for key in keys}

a = copydict(d, 'foo', 'bar')
b = copydict(d, 'foo', 'baz')
c = copydict(d, 'bar', 'baz')

Is there a better solution than the above?

davidchambers
  • 23,918
  • 16
  • 76
  • 105
  • Why don't you use a list (or a set) for a,b,c? You cold look the value up in d when you need it. – maxy Feb 18 '12 at 11:56
  • I'm actually passing `a`, `b`, and `c` to Django's `urlencode` function (which, like `urllib.urlencode`, accepts a dictionary). – davidchambers Feb 18 '12 at 11:58
  • @BlaXpirit: you should write an answer containing just "No." (pad to the minimum size with an HTML comment) so that we can all madly vote it up. – Chris Morgan Feb 18 '12 at 12:01
  • In case you're interested, I posted a related question, about going one better than copying: http://stackoverflow.com/questions/9329537/python-create-own-dict-view-of-subset-of-dictionary – Marcin Feb 18 '12 at 12:20

3 Answers3

6

I guess that the function in the question is the best solution.

Of course, someone could post some idiomatic code, but I'm sure it wouldn't work better/faster. Iterating over a list and getting items from a dict by key is as fast as it can get.

One suggestion is to remove the star from the keys parameter. Argument unpacking adds unnecessary overhead. There should be no problem with just passing those keys as a tuple.

Oleh Prypin
  • 33,184
  • 10
  • 89
  • 99
3

The only improvement I would make is to use a real function definition, not a lambda:

def copy_dict(d, *keys):
    """Make a copy of only the `keys` from dictionary `d`."""
    return {key: d[key] for key in keys}

It could be useful to deal with missing keys, but in Python 2 you can't mix optional keyword arguments with *args nicely, so you might have to go to a tuple argument:

def copy_dict(d, keys, default=None):
    """Make a copy of only the `keys` from dictionary `d`.

    Use `default` if a key is missing.

    """
    return {key: d.get(key, default) for key in keys}
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
0

As pointed out by @BlaXpirit, this solution probably isn't going to be better than yours in terms of speed or readability, but you could do something like this:

>>> from operator import itemgetter
>>> d = {'foo': True, 'bar': 42, 'baz': '!'}
>>> keys = ['foo', 'bar']
>>> dict(zip(keys, itemgetter(*keys)(d)))
{'bar': 42, 'foo': True}
jcollado
  • 39,419
  • 8
  • 102
  • 133