【发布时间】:2015-04-30 10:29:25
【问题描述】:
我有一个(可能相当大的)字典和一个“可能”键的列表。我想快速找到字典中哪些键具有匹配值。我发现很多关于单个字典值here 和here 的讨论,但没有讨论速度或多个条目。
我提出了四种方法,对于效果最好的三种方法,我在下面比较了它们在不同样本量上的速度 - 有更好的方法吗?如果人们可以提出明智的竞争者,我也会让他们接受下面的分析。
示例列表和字典的创建如下:
import cProfile
from random import randint
length = 100000
listOfRandomInts = [randint(0,length*length/10-1) for x in range(length)]
dictionaryOfRandomInts = {randint(0,length*length/10-1): "It's here" for x in range(length)}
方法一:'in'关键字:
def way1(theList,theDict):
resultsList = []
for listItem in theList:
if listItem in theDict:
resultsList.append(theDict[listItem])
return resultsList
cProfile.run('way1(listOfRandomInts,dictionaryOfRandomInts)')
0.018 秒内调用 32 个函数
方法二:错误处理:
def way2(theList,theDict):
resultsList = []
for listItem in theList:
try:
resultsList.append(theDict[listItem])
except:
;
return resultsList
cProfile.run('way2(listOfRandomInts,dictionaryOfRandomInts)')
0.087 秒内调用 32 个函数
方法三:设置交集:
def way3(theList,theDict):
return list(set(theList).intersection(set(theDict.keys())))
cProfile.run('way3(listOfRandomInts,dictionaryOfRandomInts)')
0.046 秒内调用 26 个函数
方法四:幼稚使用dict.keys():
这是一个警示故事 - 这是我的第一次尝试,到目前为止是最慢的!
def way4(theList,theDict):
resultsList = []
keys = theDict.keys()
for listItem in theList:
if listItem in keys:
resultsList.append(theDict[listItem])
return resultsList
cProfile.run('way4(listOfRandomInts,dictionaryOfRandomInts)')
12 次函数调用在 248.552 秒内
编辑:将答案中给出的建议纳入我为保持一致性而使用的同一框架中。许多人已经注意到在 Python 3.x 中可以获得更多的性能提升,尤其是基于列表理解的方法。非常感谢所有的帮助!
方法 5:更好的交叉路口方式(感谢 jonrsharpe):
def way5(theList, theDict):
return = list(set(theList).intersection(theDict))
25 次函数调用在 0.037 秒内
方法 6:列表理解(感谢 jonrsharpe):
def way6(theList, theDict):
return [item for item in theList if item in theDict]
24 次函数调用在 0.020 秒内
方法 7:使用 & 关键字(感谢 jonrsharpe):
def way7(theList, theDict):
return list(theDict.viewkeys() & theList)
25 次函数调用在 0.026 秒内
对于方法 1-3 和 5-7,我用长度为 1000、10000、100000、1000000、10000000 和 100000000 的列表/字典对它们进行了定时,并显示了所用时间的对数图。在所有长度上,交集和语句内方法的性能更好。梯度都在 1 左右(可能更高一点),表示 O(n) 或者可能稍微超线性缩放。
【问题讨论】:
-
Python 的哪个版本?在 2.x 中,
dict.keys返回一个列表,例如,我希望set(theDict.keys())比set(theDict)慢,而set(theList).intersection(theDict)比set(theList).intersection(set(theDict.keys()))快(并且timeit同意.. .) -
此外,list comprehension 通常比循环和
appending 更快。 -
为了将来参考,
[c]profile不适合做基准测试,尤其是微基准测试。事实上,the docs 顶部有一个注释说明了这一点,并建议使用timeit(就像所有答案一样)。在这种特殊情况下,除了timeit没有做的所有其他事情之外,它还增加了每个 Python 函数调用的开销,但不会增加内置函数的开销,这使得一些答案比其他答案更糟糕。跨度> -
您使用
profile的目的是确定代码的哪一部分使其变慢,而不是是否变慢。 -
另外,为什么你在编辑中提到了 Python 3.x,而不是 PyPy 或 IronPython?如果 8% 的改进值得切换语言版本,那么 700000000% 的改进肯定值得在同一语言版本中切换实现,对吧? :)
标签: python performance list dictionary