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

Python 如何在使用itertools.tee检查下一个元素时最小化空间成本?,python,itertools,tee,Python,Itertools,Tee,我试图使用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

我试图使用
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
如tee主席所述:

此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
有没有更好的方法来实现这一目标


提前谢谢

我的意思是,在你的特殊情况下,这有什么错

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)

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

这有点微妙-它取决于
tee
函数的未记录属性以及。示例Python代码将存储从创建迭代器到每个迭代器使用的所有项,但很容易想象迭代器将具有清理效果,从而放弃对队列中数据的声明。但即便如此,
del
删除了你的名字;它不能保证物体的毁灭。这样的清理工作将因此而起作用,但不一定在您预期的时间进行。要知道这是否会发生,需要阅读。它确实支持单个迭代器,这表明可以用一种方法进行优化

的CPython代码相当简单;它包含对
teedataobject
的引用,该对象最多包含57个项目,也构成一个单链接列表。因此,正常的引用计数语义适用于该批处理级别。因此,基本上,对于CPython,即使在所有迭代器都使用了56个项之后,也会在内存中保留多达56个项,但仅此而已,因为引用计数处理是即时的。只要
tee
迭代器存在,它们之间就可以容纳任意数量的项,但它们不会从源迭代器中提前读取;必须至少有一个tee迭代器已通过获取项

因此基本结论是:是的,
del
将在CPython中工作,但使用
tee
意味着您将临时存储57个项目的批次,而不是1个。重复此方法可能会导致任意数量的此类窗口-除了
tee
迭代器是可复制的,并且将共享其基础列表

这是对CPython的一个版本(4243df51fe43)的具体解释。实现在PyPy、IronPython、Jython或其他版本的CPython中会有所不同

例如,(版本cadf868)使用了一个类似的链表,每个链表有一个项目,因此不会像这个CPython版本那样批量处理


有一个值得注意的捷径可以阻止这种成本的增长:我所研究的
tee
实现都可以生成可复制的迭代器,也可以复制可复制的迭代器。因此重复应用
tee
不会创建新的迭代器层,这是
方法的一个潜在问题

请参阅@Chris_Rands tee基本上会用尽整个迭代器来创建新的迭代器–这完全不是真的。@Chris_Rands文档说“下面的Python代码有助于解释tee所做的事情(尽管实际实现更复杂,并且只使用一个底层FIFO队列)。”如果查看CPython代码中的
teedataobject\u getitem
,您会发现,如果主迭代器达到该点,它只会获取新数据
PyIter\u Next
。然后它将存储该值,直到所有
tee
s都使用了该值。请参阅Alex Martelli关于sentinel值的评论。@Chris_Rands,仅当您运行其中一个新迭代器时。例如,如果使用
a,b,c=tee(itr,3)
那么如果使用
i=next(a);deli
在执行
next(b)
next(c)
之前,您将在内存中存储
i
。最坏的情况是,如果您执行
la=list(a)
操作,那么内存中将有
len(la)
元素,直到
b
c
都向前迭代。在(…)或不同版本的CPython中,实现将有所不同。:)你所说的可复制迭代器是什么意思?可复制迭代器有一个生成自身副本的方法。迭代器的副本生成与第一个迭代器相同的输出
tee
通过根据需要存储值来生成迭代器的副本,即使迭代器没有
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu方法。它确实解决了我的特定需求,但我也对del对。非常感谢!这个答案值得投票。它适用于第二个问题“有更好的方法吗”,而我的则适用于第一个问题“一个del能否克服[球座的存储成本]”。给出已经在评论中指出的答案是一种不好的品味…@piotrdorgost老实说,我甚至没有关注你的链接。这是我独立提出的。抱歉没有先检查。我实际上发现了一个反对此表单(以及
tee
表单)的论点。PyPy和CPython
tee
实现都会复制迭代器(如果可以的话),而迭代器不会复制底层存储,
tee
迭代器是可复制的。