这对我来说并不是一项微不足道的任务,我找不到任何收据,所以也许你可以指点我或者你有一个现成的,适当的,经过精心调整的解决方案吗?正确的含义也适用于不知道自己的长度(没有__len__
)的迭代器,并且适用于可耗尽的迭代器(例如链式迭代器);调整好的意思很快。
注意:由于必须缓存迭代器输出以重新迭代它们(Glenn Maynard指出了它),因此无法实现解决方案。
使用示例:
>>> list(izip_cycle(range(2), range(5), range(3)))
[(0, 0, 0), (1, 1, 1), (0, 2, 2), (1, 3, 0), (0, 4, 1)]
>>> from itertools import islice, cycle, chain
>>> list(islice(izip_cycle(cycle(range(1)), chain(range(1), range(2))), 6))
[(0, 0), (0, 0), (0, 1), (0, 0), (0, 0), (0, 1)]
答案 0 :(得分:1)
根据您的要求,一种可能对您有用的简单方法是:
import itertools
def izip_cycle(*colls):
maxlen = max(len(c) if hasattr(c,'__len__') else 0 for c in colls)
g = itertools.izip(*[itertools.cycle(c) for c in colls])
for _ in range(maxlen):
yield g.next()
这首先找到了最长序列的长度,因此它知道要重复多少次。没有__len__
的序列被计为具有0长度;这可能是你想要的 - 如果你有一个无休止的序列,你可能想要重复有限序列。虽然这不处理没有长度的有限迭代器。
我们绝不会使用itertools.cycle
创建每个迭代器的循环版本,然后使用itertools.zip
将它们压缩在一起。
最后,我们从我们的zip中输出每个条目,直到我们给出了所需的结果数量。
如果你想让它适用于没有len
的有限迭代器,我们需要自己做更多的工作:
def izip_cycle(*colls):
iters = [iter(c) for c in colls]
count = len(colls)
saved = [[] for i in range(count)]
exhausted = [False] * count
while True:
r = []
for i in range(count):
if not exhausted[i]:
try:
n = iters[i].next()
saved[i].append(n)
r.append(n)
except StopIteration:
exhausted[i] = True
if all(exhausted):
return
saved[i] = itertools.cycle(saved[i])
if exhausted[i]:
r.append(saved[i].next())
yield r
这基本上是Python implementation of itertools.cycle
in the documentation的扩展,可以在多个序列上运行。我们查看了我们在saved
中看到的项目,以便重复并跟踪exhausted
中已经用完的序列。
由于此版本等待所有序列耗尽,如果你传递了无限的东西,循环将永远运行。
答案 1 :(得分:1)
这是受itertools.tee
和itertools.cycle
启发的内容。它适用于任何类型的迭代:
class izip_cycle(object):
def __init__(self, *iterables ):
self.remains = len(iterables)
self.items = izip(*[self._gen(it) for it in iterables])
def __iter__(self):
return self.items
def _gen(self, src):
q = []
for item in src:
yield item
q.append(item)
# done with this src
self.remains -=1
# if there are any other sources then cycle this one
# the last souce remaining stops here and thus stops the izip
if self.remains:
while True:
for item in q:
yield item
答案 2 :(得分:0)
def izip_cycle_inplace(*iterables):
def wrap(it):
empty = True
for x in it: empty = yield x
if empty: return
next(counter)
while True:
empty = True
for x in it: empty = yield x
if empty: raise ValueError('cannot cycle iterator in-place')
iterators = [wrap(i) for i in iterables]
counter = iter(iterators)
next(counter)
while True:
yield [next(i) for i in iterators]
def izip_cycle(*iterables):
def wrap(it):
elements = []
for x in it:
yield x
elements.append(x)
if not elements: return
next(counter)
while True:
for x in elements: yield x
iterators = [wrap(i) for i in iterables]
counter = iter(iterators)
next(counter)
while True:
yield [next(i) for i in iterators]