【问题标题】:What is the advantage of iteritems?ititems的优势是什么?
【发布时间】:2014-08-03 01:44:01
【问题描述】:

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

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 有什么优势。

更新: 以获得更准确的时间配置文件

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

【问题讨论】:

  • 那...不是我期望看到的
  • answer 可能值得阅读。
  • @HuuNguyen 这并不重要。他问的不是items() vs iteritems(),而是一个简单的循环+键查找和iteritems()。 (我也没有立即得到这个,因为items() vs iteritems() 经常出现......)
  • 我会添加一个 python 版本。 AFAIK 2 和 3 在这方面差别很大。
  • 使用 %timeit 与 %time 计时时,我得到不同的结果。什么是最准确的?

标签: python-2.7 dictionary iterator


【解决方案1】:

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

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

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

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

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

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

例如在代码中:

function(dictionary.iteritems())

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

dict 中检索项目非常常见,因此提供一个内置方法确实很有意义,事实上,有一个:items()items() 的问题在于:

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

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

使用iteritems() 的优势与使用items() 相比手动获取值的优势相同:

  • 您编写的代码更少,这使其更干燥并减少出错的机会
  • 代码更具可读性。

加上懒惰的好处。


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

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


我想指出 iteritems() 实际上在 python3 中已被删除。这是此版本“清理”的一部分。 Python3 items() 方法 id(大部分)等同于 Python2 的 viewitems() 方法(如果我没记错的话,实际上是一个反向移植......)。

这个版本是惰性的(因此提供了iteritems() 的替代品)并且还具有更多功能,例如提供“类似集合”的操作(例如以有效的方式查找dicts 之间的共同项目等。 ) 所以在 python3 中,使用 items() 而不是手动检索值的原因更加引人注目。

【讨论】:

  • 现在有了最新的 Python 3 子版本,viewitems() 是 Py3 的 items() 的 Py2 backport,因为两者都提供实时视图,而 iteritems() 提供调用方法时的快照。
【解决方案2】:

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

【讨论】:

  • 我最初对此表示反对,因为它不是问题的答案。不过,在回顾这个问题时,OP 是在询问“与直接访问 dict 相比,iteritems 有什么优势”,所以从技术上讲,这是一个有效的答案。
  • 鉴于 OP 发布的时间,我问自己 - 我为什么要使用这个成语?这绝对是最重要的。
【解决方案3】:

与使用系统 time 命令相反,在 ipython 中运行 timeit 会产生:

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。您是否多次运行它以确认它不是系统本身的问题?

【讨论】:

    【解决方案4】:

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

    为了彻底起见,我已经对一系列不同的配置进行了计时。这些都是使用timeit 定时的,重复因子为10。这是在具有 16GB 内存和 2.3GHz Core i7 的 Mac OS X 10.9.3 上使用 CPython 2.7.6 版。

    原配置

    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 循环,并在第一个循环中通过访问k 处的字典为变量v 赋值。

    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
    

    第一次没有赋值

    这个删除了第一个循环中的分配,但保留了字典访问权限。这不是一个公平的比较,因为第二个循环创建了一个附加变量并隐式地为其赋值。

    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
    

    有趣的是,分配对访问本身来说是微不足道的——总共只有 20 毫秒的差异。在每次比较中(即使是最终的、不公平的比较),iteritems 都胜出。

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

    【讨论】:

      【解决方案5】:

      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 
      

      【讨论】:

        猜你喜欢
        • 2015-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-25
        • 2017-06-21
        • 2013-05-15
        • 1970-01-01
        • 2011-07-15
        相关资源
        最近更新 更多