Python dict:在Python中,匹配一个数字键和一个单词之间有速度上的差异吗?
在一个包含50个项目的Python dict中,匹配一个整数键(2个数字)来查找字符串值与匹配一个字符串键(5-10个以上字母)来查找大量循环(100000个以上)上的整数值相比,会有任何已知的明显速度差异吗Python dict:在Python中,匹配一个数字键和一个单词之间有速度上的差异吗?,python,mysql,Python,Mysql,在一个包含50个项目的Python dict中,匹配一个整数键(2个数字)来查找字符串值与匹配一个字符串键(5-10个以上字母)来查找大量循环(100000个以上)上的整数值相比,会有任何已知的明显速度差异吗 作为小额奖金;在MYSQL和Python中执行这样的活动有什么好处吗 功能是一个有用的练习,但你必须带着它 一粒盐。这很难做到 以准确和有意义的方式进行基准测试 人们关心的是整体性能,而不是个人特征 表演 我发现使用“测试线束”更容易运行 以可比的方式提供不同的备选方案 对于字典查找,下面
作为小额奖金;在MYSQL和Python中执行这样的活动有什么好处吗 功能是一个有用的练习,但你必须带着它 一粒盐。这很难做到 以准确和有意义的方式进行基准测试 人们关心的是整体性能,而不是个人特征 表演 我发现使用“测试线束”更容易运行 以可比的方式提供不同的备选方案 对于字典查找,下面是一个使用PyPI模块的示例。 100 随机运行,设置N=50项的
dict
s
每个--int
键和str
值或
反向,然后尝试两种尝试
/除外
s
和get
访问范例。代码如下:
import benchmark
from random import choice, randint
import string
def str_key(length=8, alphabet=string.ascii_letters):
return ''.join(choice(alphabet) for _ in xrange(length))
def int_key(min=10, max=99):
return randint(min, max)
class Benchmark_DictLookup(benchmark.Benchmark):
each = 100 # allows for differing number of runs
def setUp(self):
# Only using setUp in order to subclass later
# Can also specify tearDown, eachSetUp, and eachTearDown
self.size = 1000000
self.n = 50
self.intdict = { int_key():str_key() for _ in xrange(self.n) }
self.strdict = { str_key():int_key() for _ in xrange(self.n) }
self.intkeys = [ int_key() for _ in xrange(self.size) ]
self.strkeys = [ str_key() for _ in xrange(self.size) ]
def test_int_lookup(self):
d = self.intdict
for key in self.intkeys:
try:
d[key]
except KeyError:
pass
def test_int_lookup_get(self):
d = self.intdict
for key in self.intkeys:
d.get(key, None)
def test_str_lookup(self):
d = self.strdict
for key in self.strkeys:
try:
d[key]
except KeyError:
pass
def test_str_lookup_get(self):
d = self.strdict
for key in self.strkeys:
d.get(key, None)
class Benchmark_Hashing(benchmark.Benchmark):
each = 100 # allows for differing number of runs
def setUp(self):
# Only using setUp in order to subclass later
# Can also specify tearDown, eachSetUp, and eachTearDown
self.size = 100000
self.intkeys = [ int_key() for _ in xrange(self.size) ]
self.strkeys = [ str_key() for _ in xrange(self.size) ]
def test_int_hash(self):
for key in self.intkeys:
id(key)
def test_str_hash(self):
for key in self.strkeys:
id(key)
if __name__ == '__main__':
benchmark.main(format="markdown", numberFormat="%.4g")
结果是:
$ python dictspeed.py
Benchmark Report
================
Benchmark DictLookup
--------------------
name | rank | runs | mean | sd | timesBaseline
---------------|------|------|--------|---------|--------------
int lookup get | 1 | 100 | 0.1756 | 0.01619 | 1.0
str lookup get | 2 | 100 | 0.1859 | 0.01477 | 1.05832996073
int lookup | 3 | 100 | 0.5236 | 0.03935 | 2.98143047487
str lookup | 4 | 100 | 0.8168 | 0.04961 | 4.65108861267
Benchmark Hashing
-----------------
name | rank | runs | mean | sd | timesBaseline
---------|------|------|----------|-----------|--------------
int hash | 1 | 100 | 0.008738 | 0.000489 | 1.0
str hash | 2 | 100 | 0.008925 | 0.0002952 | 1.02137781609
Each of the above 600 runs were run in random, non-consecutive order by
`benchmark` v0.1.5 (http://jspi.es/benchmark) with Python 2.7.5
Darwin-13.4.0-x86_64 on 2014-10-28 19:23:01.
结论:字典中的字符串查找并不比整数查找贵多少但是被认为是python式的“请求原谅而不是许可”范式比简单地使用get
方法调用花费的时间要长得多。此外,散列字符串(至少大小为8)并不比散列整数昂贵多少
但是,如果在不同的实现上运行,事情会变得更加有趣,比如PyPy:
$ pypy dictspeed.py
Benchmark Report
================
Benchmark DictLookup
--------------------
name | rank | runs | mean | sd | timesBaseline
---------------|------|------|---------|-----------|--------------
int lookup get | 1 | 100 | 0.01538 | 0.0004682 | 1.0
str lookup get | 2 | 100 | 0.01993 | 0.001117 | 1.295460397
str lookup | 3 | 100 | 0.0203 | 0.001566 | 1.31997704025
int lookup | 4 | 100 | 0.02316 | 0.001056 | 1.50543635375
Benchmark Hashing
-----------------
name | rank | runs | mean | sd | timesBaseline
---------|------|------|-----------|-----------|--------------
str hash | 1 | 100 | 0.0005657 | 0.0001609 | 1.0
int hash | 2 | 100 | 0.006066 | 0.0005283 | 10.724346492
Each of the above 600 runs were run in random, non-consecutive order by
`benchmark` v0.1.5 (http://jspi.es/benchmark) with Python 2.7.8
Darwin-13.4.0-x86_64 on 2014-10-28 19:23:57.
PyPy的速度大约是最佳情况下的11倍,但比率相差很大。PyPy不会像CPython那样承受巨大的异常处理成本。而且,对整数进行哈希运算比对字符串进行哈希运算慢10倍。如果有意外的结果呢
我本想尝试Python3,但基准测试在那里安装得不好。我还尝试将字符串长度增加到50。这并没有显著改变结果、比率或结论
总的来说,散列和查找速度非常快,除非你必须以数百万或数十亿的速度进行,或者有非常长的键,或者其他一些不寻常的情况,否则开发人员通常不必担心他们的微性能。为什么不自己测量一下呢timeit
是您的朋友。字典查找是键->值的哈希映射。检索时间应该是O(1)
@g.d.d.c-你能稍微解释一下最后一部分吗?@MihaiMaruseac Cool beans!我不知道它的存在。我想我最终可能会测试它,但我不确定这其中的任何部分是否是常识。例如,“当然,数字更快,数字/字节更少”或类似性质的任何东西。O(1)
意味着从字典中检索单个键不应随字典的大小而变化。检索任何特定值所做的工作都是常量-散列键、检查存储桶、识别冲突、返回。如果您认为有人会建议使用try更像Pythonic。除了而不是get()
,您要么误解了人,要么听错了人的话。是的,EAFP比LBYL更受鼓励,但是get()
不是LBYL的一个例子。因此,如果我读对了,如果我要进行160k次值查找(通过str键),查找本身总共需要大约4.6分钟(而不是4.95分钟),在PyPy中只需要24秒。我从来没有真正看过皮比——天哪,牛跑得真快。尽管如此,数字仍然很低。感谢您运行测试并包含代码-现在我一定会在将来运行我自己的测试。干得好@JonathanEunice@JohnY我同意这是一个坏主意,但多年来我看到了多个教程和讨论()建议对key in d
或d.get(key,None)
方法进行异常处理。几年来,这似乎是一个相当有宗教/争议的问题get
是一个很好的例子,因为它可以防止不愉快的异常结果。如果您喜欢只调用d中的键
条件LBYL,那么get
是一个更大的飞跃,但不要失败。@JonathanEunice:如果有“飞跃但不要失败”这样的事情,那么试试..除此之外
也符合该定义!我想说,把get()
看作是一种有效的try。但是我可以把get()
放在一个与try
或中的完全不同的类别中。关键是Python不是一次只选择一个二分法,总是选择二分法的一端而不是另一端。它是关于同时平衡许多相互竞争的关注点并灵活地导航它们(参见禅宗)。@JonathanEunice:get()
是Pythonic,因为如果您的用例确实符合get()
提供的内容,那么它比其他任何一种方法都具有更好的可读性和实用性。