Python:从dict中删除大量密钥的最快策略

Python:从dict中删除大量密钥的最快策略,python,performance,python-2.7,dictionary,Python,Performance,Python 2.7,Dictionary,据我所知,Python字典是一个哈希表,若表的大小超过当前表最大大小的2/3(),它就会调整大小 我需要删除很多字典项(几千个),例如,一小时一次,基于简单的标准-if key=guard\u condition} #迭代时调整大小 对于输入d: 如果钥匙= 100圈,最佳3圈:每圈2.54毫秒 在[140]中:d={item:item for item In-xrange(10000)}; 在[149]中:def del_iter(d,保护条件): ..:对于d中的键。键() ..…:如果钥匙

据我所知,Python字典是一个哈希表,若表的大小超过当前表最大大小的2/3(),它就会调整大小

我需要删除很多字典项(几千个),例如,一小时一次,基于简单的标准-if key=guard\u condition} #迭代时调整大小 对于输入d: 如果钥匙 是否有其他方法用于此目的?
哪个更快?

我尝试了IPython,结果如下:

In [140]: d = {item: item for item in xrange(10000)};

In [142]: guard_condition = 9000;

In [144]: %timeit new_d = {key: value for key, value in d.iteritems() if key >=
100 loops, best of 3: 2.54 ms per loop

In [140]: d = {item: item for item in xrange(10000)};

In [149]: def del_iter(d, guard_condition):
   .....:     for key in d.keys():
   .....:         if key < guard_condition:
   .....:             del d[key]
   .....:

In [150]: %timeit del_iter(d, guard_condition)
1000 loops, best of 3: 232 us per loop
[140]中的
:d={item:item for item In xrange(10000)};
在[142]中:防护条件=9000;
在[144]中:%timeit new_d={key:value for key,如果key>=
100圈,最佳3圈:每圈2.54毫秒
在[140]中:d={item:item for item In-xrange(10000)};
在[149]中:def del_iter(d,保护条件):
..:对于d中的键。键()
..…:如果钥匙
差异大约是100个循环*2.54 ms=254000 us与1000个循环*232 us=232000 us,对我的情况来说可以忽略不计

我将使用dict理解,因为可读性很重要,而且


正如我所看到的,执行时间是小菜一碟,我同意@Hyperboreus关于过早优化的观点。

从纯性能的角度来看,无论您是用Ruby还是Python编程,循环总是比列表理解快。但是既然您是用Python编程,您可能希望使用列表理解,因为如果速度是最重要的,你就不会用Python编程。

这取决于你的字典大小和你必须删除的元素数量:如果你删除的字典键少于80%,那么“迭代时调整大小”比“dict comprehension”快。如果你删除的字典键超过80%,那么“dict comprehension”就可以了更快。请使用此代码自己尝试

import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()

guard_condition = int(raw_input("Enter guard_condition: "))

d = {item: item for item in xrange(10000000)};

new_d = {key: value for key, value in d.iteritems() if key >= guard_condition }

def del_iter(d, guard_condition):
    for key in d.keys():
        if key < guard_condition:
            del d[key]

del_iter(d, guard_condition)

pr.disable()
s = StringIO.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()
导入cProfile、pstats、StringIO
pr=cProfile.Profile()
pr.enable()
保护条件=int(原始输入(“输入保护条件”))
d={item:xrange(10000000)中的item对应的item};
new_d={key:key的值,如果key>=guard_condition},则d.iteritems()中的值
def del_iter(d,防护条件):
对于d.keys()中的键:
如果钥匙
对于保护条件=7000000,输出为

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    2.794    2.794    2.794    2.794 {raw_input}
     1    1.263    1.263    1.263    1.263 dictDel1.py:7(<dictcomp>)
     1    1.030    1.030    1.030    1.030 dictDel1.py:9(<dictcomp>) <-- dict comprehension
     1    0.892    0.892    0.976    0.976 dictDel1.py:11(del_iter) <-- resize while iterating
     1    0.085    0.085    0.085    0.085 {method 'keys' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'iteritems' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    3.316    3.316    3.316    3.316 {raw_input}
     1    1.247    1.247    1.247    1.247 dictDel1.py:7(<dictcomp>)
     1    0.937    0.937    1.052    1.052 dictDel1.py:11(del_iter) <-- resize while iterating
     1    0.787    0.787    0.787    0.787 dictDel1.py:9(<dictcomp>) <-- dict comprehension
     1    0.115    0.115    0.115    0.115 {method 'keys' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'iteritems' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
ncalls tottime percall cumtime percall文件名:lineno(函数)
1 2.794 2.794 2.794 2.794{raw_input}
1 1.263 1.263 1.263 1.263 dictDel1.py:7()

1 1.030 1.030 1.030 1.030 dictDel1.py:9()进一步我的上述评论-使用LRU(最近使用最少)或LFU(使用最少)缓存:

当新项目出现时,请使用适当的策略确定要清除哪些项目。这将在应用程序的整个生命周期内分摊删除项目的成本,而不是偶尔有选择地删除项目


我不认为有一种使用del[key]从字典中删除的更快的方法,但是有更好的方法来实现你的目标(我猜)尝试这样做。LRU和LFU是非常流行和常用的解决方案。

您的两种方法不会产生相同的输出。第一种方法将保留所有相等于
保护条件的密钥,第二种方法将丢弃它们。“几千个密钥,每小时一次”对于计算机来说,这听起来不算多。它实际需要多长时间?您需要多长时间?字典有多大,您是删除了它的大部分还是保留了它的大部分?这是一种什么样的键值/保护条件测试?这段代码实际上是您应用程序的瓶颈吗?您知道:“过早优化…”用几千个键来配置这个,并找出可能的瓶颈,怎么样?好的@viach我也这么认为。那么你如何决定保留什么?阈值意味着什么?你可能会更好地使用LRU(最近使用最少的)排队以确定要删除的内容,并在添加新项目时动态删除,删除其他项目,而不是经常大量删除。这将随着时间的推移分摊性能损失。Python的列表理解是用C实现的,因此对于
循环,通常比
更快。但在这种情况下,循环做了一些事情完全不同——dict comprehension构建一个全新的dict,for循环从旧的dict中删除元素。没有一揽子语句会有帮助。同意,理解速度相当快。在这种情况下,问题还在于构建一个新的dict(使用dict comprehension)是否更快而不是编辑现有的。列表理解速度很快,但viach的测试显示“纯for循环”速度更快。或者你可以通过谷歌搜索查看性能差异。@RemcoGerlich:但是示例做了不同的事情。尽管即使在列表理解和显式for循环具有相同效果的情况下,显式for loop可能更快。我不会说“总是”,但根据我的经验,它通常更快。“如果速度是最重要的,你就不会用Python编程”似乎是不必要的。在许多普通程序中,你只需要加速1%的代码就可以获得时间性能的提高。通常,这1%已经用Python调用的C代码进行了优化(数据库代码、数字运算代码)或代码是I/O受限的,因此应用程序代码的速度无关紧要。您的计时显示,听写理解速度比
del_iter()
慢10倍。显著的差异可能是由于
del_iter()
修改
d
词典(1次迭代删除密钥,999次迭代不删除任何内容)。
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    3.316    3.316    3.316    3.316 {raw_input}
     1    1.247    1.247    1.247    1.247 dictDel1.py:7(<dictcomp>)
     1    0.937    0.937    1.052    1.052 dictDel1.py:11(del_iter) <-- resize while iterating
     1    0.787    0.787    0.787    0.787 dictDel1.py:9(<dictcomp>) <-- dict comprehension
     1    0.115    0.115    0.115    0.115 {method 'keys' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'iteritems' of 'dict' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}