76

Is it possible to assign values to more than one keys of a dictionary in a more concise way than the one below?

I mean, let d be a dictionary initialized as below:

d={'a':1,'b':2,'c':3}

To assign values to multiple keys I need to do this:

d['a']=10
d['b']=200
d['c']=30

Can I achieve same with something like this:

d['a','b','c']=10,200,30

Thanks.

ozi
  • 1,651
  • 2
  • 17
  • 17

5 Answers5

113

You can use dict.update:

d.update({'a': 10, 'b': 200, 'c': 30})

This will overwrite the values for existing keys and add new key-value-pairs for keys that do not already exist.

Aamir Rind
  • 38,793
  • 23
  • 126
  • 164
  • Yup, this sure works. I knew `update()`, but... Thanks. – ozi Jan 19 '14 at 21:09
  • 15
    You can also write this as `d.update(a=10, b=200, c=300)`, as long as the keys are valid identifiers (just like you can in the constructor). – abarnert Jan 20 '14 at 00:20
  • 4
    There is a faster way: `d = {**d, **dict(zip(keys, values))}`. See my comment for details: https://stackoverflow.com/a/45043651/618045 – Sklavit Jul 13 '17 at 17:40
  • @Sklavit that is however highly unpythonic – WiseStrawberry Aug 23 '17 at 10:27
  • 2
    @WiseStrawberry. Why? This is legal python syntax (Python >=3.5). – Sklavit Aug 23 '17 at 14:51
  • 1
    Not saying its not legal python syntax, its just not very readable, I think its elegant, but not readable. Just my 2 cents. In python readable code is in general prefered over complex code. – WiseStrawberry Aug 24 '17 at 11:01
  • @abarnert definitely a cool alternate, but I am wary of its readability down the line (e.g., if 'a' is a key string, then calling it as a keyword that could be anything could generate confusion). Because of that I go with the syntax in the original accepted answer. That said, good to know this alternate exists I didn't know before! – eric Oct 28 '19 at 15:34
  • @abarnert reply is great as well. Thanks for the insight. – d48 Feb 15 '20 at 23:27
15

You can also simply use the multiple assigment semantics:

d['a'], d['b'], d['c'] = 10, 200, 30
Community
  • 1
  • 1
Delgan
  • 18,571
  • 11
  • 90
  • 141
10

You can always wrap it in a function:

def multiassign(d, keys, values):
    d.update(zip(keys, values))

Even if you didn't know about update, you could write it like this:

def multiassign(d, keys, values):
    for k, v in zip(keys, values):
        d[k] = v

Or you can even write a dict subclass that gives you exactly the syntax you wanted:

class EasyDict(dict):
    def __getitem__(self, key):
        if isinstance(key, tuple):
            return [super().__getitem__(k) for k in key]
        else:
            return super().__getitem__(key)
    def __setitem__(self, key, value):
        if isinstance(key, tuple):
            self.update(zip(key, value))
        else:
            super().__setitem__(key, value)
    def __delitem__(self, key, value):
        if isinstance(key, tuple):
            for k in key: super().__delitem__(k)
        else:
            super().__setitem__(key, value)

Now:

>>> d = {'a': 1, 'd': 4}
>>> multiassign(d, ['a', 'b', 'c'], [10, 200, 300])
>>> d
{'a': 10, 'b': 200, 'c': 300, 'd': 4}
>>> d2 = EasyDict({'a': 1, 'd': 4})
>>> d2['a', 'b', 'c'] = 100, 200, 300
>>> d2
{'a': 10, 'b': 200, 'c': 300, 'd': 4}

Just be aware that it will obviously no longer be possible to use tuples as keys in an EasyDict.

Also, if you were going to use this for something serious, you'd probably want to improve the error handling. (d['a', 'b'] = 1 will give a cryptic message about zip argument #2 must support iteration, d['a', 'b', 'c'] = 1, 2 will silently work and do nothing to c, etc.)

abarnert
  • 354,177
  • 51
  • 601
  • 671
6

A speed comparison, from the worst to the best:

Python 3.5.3 |Continuum Analytics, Inc.| (default, May 15 2017, 10:43:23) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy.random as nprnd
   ...: d = dict([(_, nprnd.rand()) for _ in range(1000)])
   ...: values = nprnd.randint(1000, size=10000)
   ...: keys = nprnd.randint(1000, size=10000)
   ...: def multiassign(d, keys, values):
   ...:     for k, v in zip(keys, values):
   ...:         d[k] = v
   ...:
   ...: d1 = dict(d)
   ...: %timeit multiassign(d1, keys, values)
   ...: d1 = dict(d)
   ...: %timeit {**d1, **{keys[i]: values[i] for i in range(len(keys))}}
   ...: d1 = dict(d)
   ...: %timeit d1.update(zip(keys, values))
   ...: d1 = dict(d)
   ...: %timeit {*d1.items(), *zip(keys, values)}
   ...: d1 = dict(d)
   ...: %timeit {**d1, **{key: value for key, value in zip(keys, values)}}
   ...: d1 = dict(d)
   ...: %timeit {**d1, **dict(zip(keys, values))}
4 ms ± 25.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.66 ms ± 29.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.17 ms ± 31.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.81 ms ± 98.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.38 ms ± 75.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.96 ms ± 21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

So the clear winner is recreation of dictionary from dictionaries.

Sklavit
  • 2,225
  • 23
  • 29
  • Running the same test (on M1), I get different results: 934 µs ± 9.61 µs per loop; 1.3 ms ± 23.8 µs per loop; 744 µs ± 1.3 µs per loop; 1.02 ms ± 2.17 µs per loop; 816 µs ± 955 ns per loop; 693 µs ± 1.51 µs per loop; You can improve update's performance by instantiating a dict in advance; `d1.update(dict(zip(keys, values)))` has the same runtime as `{**d1, **dict)(zip(keys, values)}` `{**d1, **d2}` would also be less effective relative to `update` in a situation where you are repeatedly updating the dict with new keys and thus making larger dicts. Here you only overwrite keys. – Zorgoth Mar 21 '23 at 20:05
  • I suppose it depends on Python version. Which version did you use? – Sklavit Apr 13 '23 at 18:24
  • Unfortunately, I didn't record anything about these experiments beyond what's in my comment and I don't remember which environment I used. It might also have something to do with CPU architecture. – Zorgoth May 23 '23 at 15:02
3

You could use a dictionary comprehension e.g.

letters, nos = ['a','b','c'], [1,2,3]
d = {letters[i]: nos[i] for i in range(len(nos))}

output:

{'a': 1, 'c': 3, 'b': 2}

ChrisProsser
  • 12,598
  • 6
  • 35
  • 44