代码之家  ›  专栏  ›  技术社区  ›  NI6

如何在使用itertools.tee检查下一个元素时最小化空间成本?

  •  4
  • NI6  · 技术社区  · 7 年前

    我想用 itertools.tee 要知道迭代器是否为空而不完全消费它,请执行以下操作:

    from itertools import tee
    def get_iterator(i):
        i1, i2 = tee(i, 2)
        if next(i1, None) is None:
           # iterator is empty - raises some error
           pass
        return i2 # return not empty iterator to caller
    

    作为 docs 三通状态:

    此itertool可能需要大量的辅助存储(取决于需要存储多少临时数据)。一般来说,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,那么使用list()而不是tee()会更快。

    所以很明显,当我不是空的时候,i2在i1之前使用了大部分数据。 一个简单的del能克服这个问题吗?:

    from itertools import tee
    def get_iterator(i):
        i1, i2 = tee(i, 2)
        if next(i1, None) is None:
           # iterator is empty - raises some error
           pass
        del i1  # Does this overcome storage issue?
        return i2  # return not empty iterator to caller
    

    有没有更好的方法来实现这个目标?

    事先谢谢!

    2 回复  |  直到 7 年前
        1
  •  4
  •   Yann Vernier    7 年前

    这有点微妙——这取决于 tee 功能以及 garbage collector . 示例python代码将从创建迭代器的位置存储所有项,直到每个迭代器使用这些项为止,但可以很容易地想象迭代器将具有清除效果,从而将它们对队列中的数据的声明丢弃。但即便如此, del 移除你的名字;它不能保证物体的毁灭。这样的清理工作将因此而起作用,但不一定在您预期的时间内起作用。知道这是否发生需要阅读 the source code for tee . 它确实有 weak reference 对单个迭代器的支持,这意味着这种优化可以通过一种方式完成。

    的cpython代码 tee_next 相当简单;它引用了 teedataobject ,这是一批多达57个项目,也形成了一个单链表。因此,正常的引用计数语义适用于该批处理级别。因此,基本上,对于cpython,多达56个项保存在内存中,甚至 之后 它们被 全部的 迭代器,但不能超过这个值,因为引用计数处理是立即的。只要 球座 迭代器存在,它们之间任何数量的项都可以保留,但它们不会从源迭代器中提前读取;至少有一个tee迭代器必须通过 teedataobject_getitem .

    所以基本结论是:是的, 德尔 将在cpython中工作,但使用 球座 意思是你暂时储存了57件物品,而不是1件。重复此方法可能导致任意数量的此类窗口-除非 球座 迭代器是可复制的,并将共享其基础列表。

    这是对cpython的一个版本(4243df51fe43)的具体解释。实现将在pypy、ironpython、jython或其他cpython版本中有所不同。

    例如, PyPy's tee (版本cadf868)使用一个类似的链接列表,每个链接一个项目,所以不要像这个cpython版本那样成批地进行处理。

    有一个显著的捷径可以阻止这种成本的增长:两者 球座 我研究的实现产生了可复制的迭代器,也产生了可复制的迭代器。如此反复的应用 球座 不会创建新的迭代器层,这是 chain 接近。

        2
  •  3
  •   FHTMitchell    7 年前

    我是说,在你的具体情况下,这有什么问题

    from itertools import chain
    def get_iterator(i):
        try:
            first = next(i):
        except StopIteration:
           # iterator is empty - raises some error
           pass
        return chain([first], i)
    

    它执行完全相同的操作,但不存储除第一个值以外的任何内容。

    推荐文章