python izip 循环遍历所有可迭代对象,直到最长的结束
这对我来说并不是一件简单的事情,我找不到任何现成的解决方案,所以也许你能给我指个方向,或者你有一个现成的、合适的、经过调试的解决方案?这里的“合适”是指它也能适用于那些不知道自己长度的迭代器(没有 __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)]
3 个回答
在编程中,有时候我们需要让程序在特定的条件下执行某些操作。这就像给程序设定了一些规则,只有当这些规则被满足时,程序才会做出反应。
比如说,你可能想要在用户输入一个数字时,程序才进行计算。如果用户输入的不是数字,程序就应该给出提示,告诉用户输入不正确。这种情况下,我们就需要使用条件判断来检查用户的输入。
条件判断就像是在问一个问题:如果这个条件成立,那么就执行某个操作;如果不成立,就执行另一个操作。这样可以让程序更加智能,能够根据不同的情况做出不同的反应。
总之,条件判断是编程中非常重要的一部分,它帮助我们控制程序的行为,让程序能够根据实际情况做出适当的反应。
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]
这里有一个简单的方法,可能适合你的需求:
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
把它们组合在一起。
最后,我们会从组合中逐个输出结果,直到达到我们想要的结果数量。
如果你想让这个方法适用于没有 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文档中 itertools.cycle
的实现 的扩展,可以处理多个序列。我们把已经看到的项目保存在 saved
中以便重复,并在 exhausted
中跟踪哪些序列已经用完。
因为这个版本会等到所有序列都用完,所以如果你传入一个无限的序列,循环就会一直进行下去。
这里有一些灵感来自于 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