python字典中long(str)键的效率

时间:2015-01-26 12:22:12

标签: python dictionary key

我正在解析一些xml(带有一些python 3.4代码),并希望从节点及其id属性中检索文本。例: <li id="12345"> Some text here </li> 我当前的代码仅围绕文本构建(我现在添加了id,但之前并不需要这个)。我循环浏览一个文本/句子列表,然后继续做一些事情。所以我想创建一个以文本/句子为键的字典,并将此id属性作为值。

然而,这并不是非常有效。文本可以是整个段落,使得密钥很长。而id总是相当有限的长度(但仍然是str类型,例如一些字母字符后跟一些数字)。 但是,使id成为键,文本值需要对代码进行一些重写。所有这些都不是很有问题,但这让我感到疑惑:将文本(可能是整个段落)作为关键词的效率是多么低效,与像#&lt;&#34; ulp_887362487687678&#34;作为关键?

我可以制作两个反向词典(一个以id作为键,另一个以text作为键)并比较构造和查找以及所有。我还发现了关键长度限制(Do Dictionaries have a key length limit?)的一些主题。但我只是想知道你对此有何看法。在你的词典中有这么长的str键你肯定想要避免,或者这不是一个大问题? 如果你可以分享一些专业人士,那就太棒了!

3 个答案:

答案 0 :(得分:11)

不,Python字符串长度几乎不会对字典性能产生影响。字符串长度唯一可能影响的是hash()函数,它将键映射到哈希表槽。

字符串长度对hash()的性能影响很小:

>>> import random
>>> from timeit import timeit
>>> from string import ascii_letters
>>> generate_text = lambda len: ''.join([random.choice(ascii_letters) for _ in xrange(len)])
>>> for i in range(8):
...     length = 10 + 10 ** i
...     testword = generate_text(length)
...     timing = timeit('hash(t)', 'from __main__ import testword as t')
...     print 'Length: {}, timing: {}'.format(length, timing)
... 
Length: 11, timing: 0.061537027359
Length: 20, timing: 0.0796310901642
Length: 110, timing: 0.0631730556488
Length: 1010, timing: 0.0606122016907
Length: 10010, timing: 0.0613977909088
Length: 100010, timing: 0.0607581138611
Length: 1000010, timing: 0.0672461986542
Length: 10000010, timing: 0.080118894577

我停止生成一个包含1000万个字符的字符串,因为我无法等待笔记本电脑生成1亿个字符串。

时间几乎是不变的,因为一旦计算出来,该值实际上会缓存在字符串对象上。

答案 1 :(得分:1)

对于字符串,hash()的性能确实为O(n)但结果缓存在字符串中-重复调用将使用缓存的值。这是可能的,因为字符串是不可变的。 Martijn的代码使用timeit repeating 功能,因此您看不到这种效果,因为在最后一种情况下,在10000010中,没有10000009次计算哈希码。

下面仍然是(大约)O(n)

import random
from timeit import timeit

for i in range(10):
    length = 10 ** i
    # notice number=1 !!!
    timing = timeit('hash(t)', 't = "a" * {}'.format(length), number=1)
    print('Length: {:10d}, timing: {:.20f}'.format(length, timing))

Length:          1, timing: 0.00000437500057159923
Length:         10, timing: 0.00000287900184048340
Length:        100, timing: 0.00000342299972544424
Length:       1000, timing: 0.00000459299917565659
Length:      10000, timing: 0.00002153400055249222
Length:     100000, timing: 0.00006719700104440562
Length:    1000000, timing: 0.00066680999952950515
Length:   10000000, timing: 0.00673243699930026196
Length:  100000000, timing: 0.04393487600100343116
Length: 1000000000, timing: 0.39340837700001429766

差异是由于时序错误,分支预测等所致。

答案 2 :(得分:0)

在大多数情况下,@ Martijn Pieters的回答是正确的,即从理论上讲。 但是,实际上,在性能方面您要考虑很多事情。

我最近遇到了将长字符串散列为键的问题,并且由于python的字典键散列,我在练习中遇到了超时错误。我知道这一点是因为我使用JavaScript对象作为“字典”解决了这个问题,并且效果很好,这意味着没有超时错误。

然后,因为我的键实际上是一长串数字列表,所以我改为使它们成为数字元组(不可变对象可以是键)。那也很好。

话虽这么说,我使用示例中编写的哈希函数@Martijn Pieters和一长串数字列表作为元组版本的键来测试了计时。 tuples版本花费更长的时间在其python编译器repl.it上。我说的不是0.1差异。这是0.02和12.02之间的差异

是不是很奇怪?! :>

现在要指出的是,每个环境都不同。您的操作量不断累积。因此,您不能简单地说出某些操作将花费更长或更短的时间。即使是0.01秒的操作,也只能执行1000次,使用户等待10秒。

对于任何生产环境,如果需要,您确实想尝试优化算法,并始终使用更好的设计。对于普通软件,它可以节省用户的宝贵时间。对于云服务,这将是我们正在谈论的钞票。

最后,我绝对请勿建议仅将长字符串用作键,因为我在不同环境中得到的结果不一致。您绝对希望将id用作键,并在需要时遍历字符串值以查找id。但是,如果必须将长字符串用作键,请考虑限制字典上的操作数。保留两个版本绝对是浪费空间/ RAM。性能和内存主题是另一堂课。