iteritems有什么优势?

时间:2014-06-12 16:25:08

标签: python-2.7 dictionary iterator

我正在使用Python 2.7.5 @ Mac OS X 10.9.3,8GB内存和1.7GHz Core i5。我测试了时间消耗如下。

d = {i:i*2 for i in xrange(10**7*3)} #WARNING: it takes time and consumes a lot of RAM

%time for k in d: k,d[k]
CPU times: user 6.22 s, sys: 10.1 ms, total: 6.23 s
Wall time: 6.23 s

%time for k,v in d.iteritems(): k, v
CPU times: user 7.67 s, sys: 27.1 ms, total: 7.7 s
Wall time: 7.69 s

似乎iteritems更慢。 我想知道iteritems比直接访问dict有什么好处。

更新: 获得更准确的时间档案

In [23]: %timeit -n 5 for k in d: v=d[k]
5 loops, best of 3: 2.32 s per loop

In [24]: %timeit -n 5 for k,v in d.iteritems(): v
5 loops, best of 3: 2.33 s per loop

5 个答案:

答案 0 :(得分:15)

要回答您的问题,我们应首先挖掘一些有关如何以及何时将iteritems()添加到API中的信息。

iteritems()方法 在语言中引入iteratorsgenerators之后,在Python2.2中添加了该内容(另请参阅: What is the difference between dict.items() and dict.iteritems()?)。事实上,该方法在PEP 234中被明确提及。因此它被引入作为已存在的items()的惰性替代方案。

这与我在Python 2.1中引入的file.xreadlines()file.readlines()相同的模式(顺便说一下,已经在python2.3中弃用)。

在python 2.3中添加了itertools模块,它将惰性对应项引入mapfilter等。

换句话说,当时(并且仍然存在)强烈倾向于操作的懒惰。其中一个原因是提高内存效率。另一个是避免不必要的计算。

我找不到任何引用说它是为了提高循环字典的速度而引入的。它只是用于替换实际上不必返回列表的items()调用。请注意,这包括更多用例,而不仅仅是简单的for循环。

例如在代码中:

function(dictionary.iteritems())

您不能像在示例中那样简单地使用for循环来替换iteritems()。您必须编写一个函数(或使用genexp,即使在引入iteritems()时它们不可用,它们也不会干......)。

dict中检索项目的工作经常进行,因此提供内置方法确实有意义,实际上有一个:items()items()的问题在于:

  • 它不是懒惰的,这意味着在大dict上调用它可能需要相当长的时间
  • 需要大量的记忆。如果调用包含大多数被操作对象的非常大的dict,它几​​乎可以使程序的内存使用量翻倍。
  • 大部分时间只迭代一次

因此,在引入迭代器和生成器时,很明显只需添加一个惰性对应物。如果您需要一个项目列表,因为您想要对其进行索引或多次迭代,请使用items(),否则您只需使用iteritems()并避免上述问题。

使用iteritems()的优势与使用items()相比,而不是手动获取值:

  • 您编写的代码更少,这样可以减少干扰并减少出错的可能性
  • 代码更具可读性。

加上懒惰的优点。


正如我已经说过的,我无法重现你的表现结果。在我的机器上iteritems() 总是比迭代+按键查找更快。无论如何,差异可以忽略不计,这可能是由于操作系统如何处理缓存和内存。换句话说,关于效率的论证并不是反对(也不赞成)使用一种或另一种替代方案的强烈论据。

鉴于平均性能相同,请使用最易读,最简洁的替代方案:iteritems()。这个讨论类似于问“为什么当你可以通过索引以相同的性能循环时使用foreach?”。 foreach的重要性不在于您迭代速度更快,而是避免编写样板代码并提高可读性。


我想指出iteritems()实际上是在python3中删除的。这是该版本“清理”的一部分。 Python3 items()方法id(大部分)等同于Python2的viewitems()方法(如果我没弄错的话,实际上是一个backport)。

此版本是懒惰的(因此提供了iteritems()的替代)并且还具有其他功能,例如提供“类似集合”的操作(例如在dict之间查找公共项目因此在python3中使用items()而不是手动检索值的原因更加引人注目。

答案 1 :(得分:14)

使用具有更多描述性名称的for k,v in d.iteritems()可以使循环套件中的代码更易于阅读。

答案 2 :(得分:9)

而不是使用系统time命令,在带有timeit的ipython中运行产生:

d = {i:i*2 for i in xrange(10**7*3)} #WARNING: it takes time and consumes a lot of RAM

timeit for k in d: k, d[k]
1 loops, best of 3: 2.46 s per loop

timeit for k, v in d.iteritems(): k, v
1 loops, best of 3: 1.92 s per loop

我在windows上运行了这个,python 2.7.6。你有多次运行它以确认它不是系统本身发生的事情吗?

答案 3 :(得分:6)

我从技术上知道这不是问题的答案,但评论部分是放置此类信息的不好的地方。我希望这有助于人们更好地理解所讨论问题的本质。

为了彻底,我定时了一堆不同的配置。这些都是使用重复因子为timeit的{​​{1}}进行计时的。这是在Mac OS X 10.9.3上使用CPython 2.7.6版本,配备16GB内存和2.3GHz Core i7。

原始配置

10

Bakuriu的建议

此建议涉及传入python -m timeit -n 10 -s 'd={i:i*2 for i in xrange(10**7*3)}' 'for k in d: k, d[k]' >> 10 loops, best of 3: 2.05 sec per loop python -m timeit -n 10 -s 'd={i:i*2 for i in xrange(10**7*3)}' 'for k, v in d.iteritems(): k, v' >> 10 loops, best of 3: 1.74 sec per loop 循环,并通过访问iteritems处的字典为第一个循环中的变量v分配值。

k

第一个

中没有作业

这个删除第一个循环中的赋值,但保持字典访问。这不是一个公平的比较,因为第二个循环创建了一个额外的变量并隐式赋值。

python -m timeit -n 10 -s 'd={i:i*2 for i in xrange(10**7*3)}' 'for k in d: v = d[k]'
>> 10 loops, best of 3: 1.29 sec per loop

python -m timeit -n 10 -s 'd={i:i*2 for i in xrange(10**7*3)}' 'for k, v in d.iteritems(): pass'
>> 10 loops, best of 3: 934 msec per loop

有趣的是,这项任务对于访问本身来说是微不足道的 - 差异仅为20毫秒。在每次比较中(即使是最终的,不公平的),python -m timeit -n 10 -s 'd={i:i*2 for i in xrange(10**7*3)}' 'for k in d: d[k]' >> 10 loops, best of 3: 1.27 sec per loop 胜出。

原始配置中的时间百分比最接近。这可能是由于大部分工作正在创建元组(未在任何地方分配)。一旦将其从等式中移除,两种方法之间的差异就会变得更加明显。

答案 4 :(得分:0)

dict.iter()在python 3.5中大量胜出。

这是一个小的表现统计数据:

d = {i:i*2 for i in range(10**3)}
timeit.timeit('for k in d: k,d[k]', globals=globals())
75.92739052970501
timeit.timeit('for k, v in d.items(): k,v', globals=globals())
57.31370617801076