我有一本字典:
d = {'a':1, 'b':2, 'c':3, 'd':4}
然后我有一个键列表:
l = ['a', 'b', 'z']
我想要的结果是:
[1, 2, None]
我到目前为止所做的是:
[d.get(k) for k in l]
有更快的方法吗?也许没有for
?
答案 0 :(得分:13)
您可以使用:
>>> list(map(d.get, l))
[1, 2, None]
它有两个优点:
d.get
查询 - 而不是每次迭代dict.get
是用C实现的,而map
是用C实现的,所以它可以避免函数调用中的Python层(粗略地说,细节有点复杂)。至于时间(在Jupyter笔记本中的Python 3.6上执行):
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)
请注意,在这种情况下,这实际上更慢!这是因为对于短迭代,map
和list
开销占主导地位。因此,如果您希望在短期迭代中更快地坚持您的方法。
如果l
更长,您会看到list(map(...))
最终变得更快:
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)
然而,这仍然是"只是"比较快2倍。
答案 1 :(得分:5)
重要的是要注意这里的瓶颈不是字典的大小,而是键列表的大小(当然还有方法查找时间,哈希查找键的时间,等)
请考虑以下事项:
from timeit import Timer
d = {1: 'a'}
keys = [1 for _ in range(1000)]
def _map():
return list(map(d.get, keys))
def _map_single_lookup():
g = d.get
return list(map(g, keys))
def _list_comp():
return [d.get(key) for key in keys]
def _list_comp_single_lookup():
g = d.get
return [g(key) for key in keys]
print(min(Timer(_map).repeat(100, 100)))
print(min(Timer(_map_single_lookup).repeat(100, 100)))
print(min(Timer(_list_comp).repeat(100, 100)))
print(min(Timer(_list_comp_single_lookup).repeat(100, 100)))
# 0.009307396466818774
# 0.009261678214412816
# 0.018456645101335933
# 0.011634828724497837
答案 2 :(得分:2)
更快的替代方法是将itemgetter
与defaultdict
一起使用(因为如果密钥不存在,itemgetter
不支持dict.get
之类的默认值)
from collections import defaultdict
from timeit import Timer
from operator import itemgetter
d = defaultdict(lambda: None)
d[1] = 'a'
keys = [1 for _ in range(1000)]
def _map():
return list(map(d.get, keys))
def _getter():
return list(itemgetter(*keys)(d))
print(min(Timer(_map).repeat(100, 100)))
print(min(Timer(_getter).repeat(100, 100)))
# 0.0074976040767260055
# 0.0021861597102568187
编辑为不存在的键(整数和字符串)添加了计时。对绩效没有重大影响。
from collections import defaultdict
from timeit import Timer
from operator import itemgetter
d = defaultdict(lambda: None)
d[1] = 'a'
non_existing_keys_int = [2 for _ in range(1000)]
non_existing_keys_string = ['a' for _ in range(1000)]
def get_non_existing_keys_int():
return list(itemgetter(*non_existing_keys_int)(d))
def get_non_existing_keys_string():
return list(itemgetter(*non_existing_keys_string)(d))
print(min(Timer(get_non_existing_keys_int).repeat(100, 100)))
print(min(Timer(get_non_existing_keys_string).repeat(100, 100)))
# 0.002647169132724954
# 0.002408539023506795