5

文档的配方部分itertools以下文本开头:

扩展工具提供与底层工具集相同的高性能。卓越的内存性能是通过一次处理一个元素来保持的,而不是一次将整个可迭代对象全部放入内存。通过以有助于消除临时变量的功能样式将工具链接在一起,可以保持较小的代码量。通过更喜欢“矢量化”构建块而不是使用导致解释器开销的 for 循环和生成器来保持高速。

问题是,应该如何构造生成器来避免开销?您能否提供一些具有这种开销的构造不良的块的示例?

我决定问我什么时候回答这个问题,我不能确切地说是否chain(sequence, [obj])有开销chain(sequence, repeat(obj,1)),我应该更喜欢后者。

4

1 回答 1

5

文档文本不是关于如何构造生成器以避免开销。它解释了正确编写itertools的代码,例如示例中提供的代码,完全避开了 for 循环和生成器,将其留给 itertools 或内置收集器(例如list)来使用迭代器。

举个tabulate例子:

def tabulate(function, start=0):
    "Return function(0), function(1), ..."
    return imap(function, count(start))

写这个的非向量化方式是:

def tabulate(function, start=0):
    i = start
    while True:
        yield function(i)
        i += 1

这个版本“产生解释器开销”,因为循环和函数调用是在 Python 中完成的。

关于链接单个元素,可以安全地假设这chain(sequence, [obj])会(微不足道)更快,因为固定长度的列表构造在 Python 中使用专门的语法和操作码得到了很好的优化。同样,chain(sequence, (obj,))它会更快,因为元组共享列表的优化,并且启动时更小。python -m timeit与基准测试一样,衡量比猜测要好得多。

文档引用不关心迭代器创建的差异,例如通过选择repeat(foo, 1)和之一表现出来的差异[foo]。由于迭代器只产生一个元素,因此它的使用方式没有区别。在处理可以产生数百万个元素的迭代器时,文档谈到了处理器和内存效率。与此相比,选择创建速度更快的迭代器是微不足道的,因为创建策略可以随时更改。另一方面,一旦代码设计为使用不矢量化的显式循环,以后如果不完全重写就很难更改。

于 2012-10-21T22:13:24.157 回答