我正在尝试使用 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开球状态:
This itertool may require significant auxiliary storage (depending on how much temporary data needs to be stored). In general, if one iterator uses most or all of the data before another iterator starts, it is faster to use list() instead of tee().
所以很明显,当 i 不为空时,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
有没有更好的方法来实现这个目标?
提前致谢!
最佳答案
这有点微妙 - 它取决于 tee
函数的一个未记录的属性以及 garbage collector 的一个故意模糊的属性.示例 Python 代码将存储从迭代器创建点到它们被每个迭代器使用的所有项,但人们可能很容易想象迭代器会产生清理效果,从而放弃对队列中数据的声明。但即便如此,del
还是删除了你的名字;它不保证对象的破坏。因此,这样的清理会起作用,但不一定会在您期望的时候起作用。知道这是否会发生需要阅读 the source code for tee
.它确实有 weak reference对单个迭代器的支持,提出了一种可以完成此优化的方法。
tee_next
的 CPython 代码相当简单;它包含对 teedataobject
的引用,该对象最多包含 57 个项目,也形成一个单向链表。因此,正常的引用计数语义适用于该批处理级别。所以基本上,对于 CPython,即使它们已被所有迭代器消耗,最多 56 个项目仍保留在内存中,但仅此而已,因为引用计数处理是立即的。只要 tee
迭代器存在,它们之间的任意数量的项都可以保存,但它们不会从源迭代器中提前读取;至少有一个 tee 迭代器必须通过 teedataobject_getitem
获取了项目.
所以基本判断是:是的,del
将在 CPython 中工作,但使用 tee
意味着您临时存储 57 个项目的批处理,而不是 1 个。重复此操作方法可能会导致任意数量的此类窗口 - 除了 tee
迭代器是可复制的,并将共享它们的基础列表。
这是对CPython的一个版本(4243df51fe43)的具体解释。实现将有所不同,例如PyPy、IronPython、Jython 或其他版本的 CPython。
例如,PyPy's tee (版本 cadf868)使用类似的链表,每个链接一个项目,因此不会像此 CPython 版本那样进行批处理。
有一个显着的捷径可以防止这种成本增加:我检查过的两个 tee
实现都产生可复制的迭代器,也产生可复制的迭代器。因此,重复应用 tee
不会创建新的迭代器层,这是 chain
方法的一个潜在问题。
关于python - 使用 itertools.tee 检查下一个元素时如何最小化空间成本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50599850/