使用迭代器提供了什么(如果有的话)性能优势。它似乎是解决许多问题的“正确方法”,但是它是否会创建更快/更具记忆意识的代码?我在Python中专门思考,但不限制答案。
答案 0 :(得分:20)
在python邮件列表上有一个非常好的邮件:Iterators vs Lists。它有点过时(从2003年开始),但据我所知,它仍然有效。
以下是摘要:
对于小型数据集,迭代器和基于列表的方法具有相似之处 性能。 对于较大的数据集,迭代器可以节省时间和空间。
我从中得出的是:如果可能的话,迭代器优先于将数据加载到列表中。但是除非你有一个大的数据集,否则不要扭曲你的代码来制作一个适合列表的东西来处理迭代器。
答案 1 :(得分:12)
对于Python,生成器将更快并具有更好的内存效率。试想一下range(1000)
vs xrange(1000)
的例子(这已在3.0中更改,范围现在是生成器)。使用Range,您可以预先构建列表,但XRange只有一个生成器对象,并在需要时生成下一个项目。
在小事情上,性能差异并不大,但是一旦你开始越来越多地获得越来越大的信息,你会很快注意到它。此外,不仅需要生成然后逐步执行,您将为预先构建的项目消耗额外的内存 - 与生成器表达式一样,一次只能生成1个项目。
答案 2 :(得分:7)
迭代器的主要好处不在于性能。根据我的经验,最高性能的解决方案是创建一个嵌入您选择的数据结构的算法。迭代器的好处是它们允许您解耦数据和算法,因此可以概括和重用两者。如果这也可以在没有(或很少)性能下降的情况下完成,那么它就是净增益。
我最喜欢的迭代器用法示例可以在C ++ Standard Template Library中找到。它通过在不牺牲性能的情况下干净地分离容器和算法来设法展示抽象的力量和美感。理解这种设计对我对代码的思考方式产生了深远的影响。
答案 3 :(得分:3)
range
与xrange
效果python25 -mtimeit "for i in xrange(1000): pass"
10000 loops, best of 3: 56.3 usec per loop
python25 -mtimeit "for i in range(1000): pass"
10000 loops, best of 3: 80.9 usec per loop
python26 -mtimeit "for i in xrange(1000): pass"
10000 loops, best of 3: 48.8 usec per loop
python26 -mtimeit "for i in range(1000): pass"
10000 loops, best of 3: 68.6 usec per loop
btw,range()
和xrange()
都不是迭代器:
>>> hasattr(range(1), 'next')
False
>>> hasattr(xrange(1), 'next')
False
>>> iter(xrange(1))
<rangeiterator object at 0x0097A500>
>>> iter(range(1))
<listiterator object at 0x00A7BFD0>
>>> iter([])
<listiterator object at 0x00A7BE30>
>>> iter(i for i in (1,))
<generator object at 0x00A7F940>
>>> (i for i in (1,))
<generator object at 0x00A7FDC8>
答案 4 :(得分:2)
迭代器只是实现a particular interface的类,特别是转到下一个的接口。在Python中,列表,元组,dicts,字符串和文件都实现了这个接口。如果它们的实现效果不佳,可能会导致性能不佳,但界面没有任何固有的暗示性能好坏。
答案 5 :(得分:2)
我从上面的许多答案得出的推论是“使用列表进行编码。如果需要,使用迭代器重新计算”除非你有一个大数据集,否则差别不明显。
需要注意的另一点是,即使经常使用列表,我们运行的数据集也会越来越小。
答案 6 :(得分:1)
迭代器只是一个提供允许遍历集合的方法的对象。您可以使用相同的接口遍历数组的所有元素或树的所有节点。树和数组是非常不同的数据结构,需要不同的方法来遍历..但是使用迭代器,你可以以相同的方式遍历所有元素。
对于一种类型的集合,可能还有不同的遍历方法,单个集合可能有多个迭代器。您可以使用深度优先迭代器或广度优先迭代器遍历树结构并返回节点以不同的顺序。 迭代器不是为了性能......而是通常为遍历结构提供一致的接口。
答案 7 :(得分:0)
我认为有一个答案会混淆发生器和迭代器的概念。所以我决定试着用一个比喻的例子回答这个问题。
我在厨房工作,老板给我一个加重10(或100或100万)面包重量的任务。我有一个比例和一个计算器(我算法的魔术技巧)。下面是可迭代对象,生成器,迭代器,方法差异:
可迭代对象: 每个面包都存放在一个盒子(记忆)中,我称第一个(或第0个)面包,减轻它的重量,然后将面包放回盒子里,然后去下一个面包,称重并放回去,最后,我得到了整体重量,10个(100或100万)面包仍在他们的盒子里。
发电机: 没有足够的盒子存放所有这些面包,所以我要求面包师(发电机)的帮助,他做第一个面包,给我,我称重,把结果放下,把面包扔掉,再问他另一个,等等,直到我拿到最后一块面包(或者面包用面粉)。最后,我有结果,没有面包在那里。但是谁在乎,我的老板只要我称这些面包,他没有说我不能扔掉它们(多么精彩的男孩)。
迭代: 我问某人(迭代者)帮我把第一块面包放到秤上,称重,然后把结果放下来。这个人会抓住下一个用于测量,打开和打开等等。我实际上不知道是否有人(迭代器)从盒子或面包师那里获得面包。最终,我得到了整体的重量,这对我来说并不重要。
无论如何,总结一下:
可重复对象需要一些内存来存储数据。最后,数据仍然存在。
生成器不需要内存来存储数据,它会随时生成数据。
迭代器是算法与其数据之间的通道。该数据可能已经存在并存储在存储器中,或者可以由发生器在运行中生成。在第一种情况下,当迭代器继续迭代时,该内存将被逐位释放。所以我同意上面的答案,迭代器很好,因为它的抽象可以隔离算法和数据。
python并不像这样工作。希望它有助于澄清一点。
答案 8 :(得分:0)
稍微偏离主题但是通常在迭代器上使用列表会增加更多的权重:使用迭代器更容易产生副作用,请考虑这一点:
def foo(arg: Iterable[str]):
print(list(arg)) # side effect: arg is exhausted at this point
...
你可以说测试应该抓住这个,但有时却没有。列表没有这个问题,因为它们是无状态的(在迭代意义上)。