I have a dictionary:
d = {'a':1, 'b':2, 'c':3, 'd':4}
Then I have a list of keys:
l = ['a', 'b', 'z']
My desired result is:
[1, 2, None]
What I’m doing so far is:
[d.get(k) for k in l]
Is there a faster way? Perhaps without for
?
Advertisement
Answer
You could use:
>>> list(map(d.get, l)) [1, 2, None]
It has two advantages:
- It performs the
d.get
lookup only once – not each iteration - Only CPython: Because
dict.get
is implemented in C andmap
is implemented in C it can avoid the Python layer in the function call (roughly speaking the details are a bit more complicated).
As for timings (performed on Python 3.6 in a Jupyter notebook):
d = {'a':1, 'b':2, 'c':3, 'd':4} l = ['a', 'b', 'z'] %timeit list(map(d.get, l)) 594 ns ± 41.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %timeit [d.get(k) for k in l] 508 ns ± 17.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Note that this is actually slower in this case! That’s because for short iterables the map
and list
overhead dominate. So if you want it faster on short iterables stick with your approach.
With longer l
you see that list(map(...))
eventually becomes faster:
d = {'a':1, 'b':2, 'c':3, 'd':4} l = [random.choice(string.ascii_lowercase) for _ in range(10000)] %timeit list(map(d.get, l)) 663 µs ± 64.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit [d.get(k) for k in l] 1.13 ms ± 7.55 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
However that’s still “just” a factor of 2 faster.