在Python中替换从不同迭代器获取值的最有效方法是什么,例如,alternate(xrange(1, 7, 2), xrange(2, 8, 2))
将产生1,2,3,4,5,6。我知道一种实现方法它会是:
def alternate(*iters):
while True:
for i in iters:
try:
yield i.next()
except StopIteration:
pass
但是有更高效或更清洁的方式吗? (或者,更好的是,我错过了itertools
函数?)
答案 0 :(得分:16)
对于“干净”实施,您需要
itertools.chain(*itertools.izip(*iters))
但也许你想要
itertools.chain(*itertools.izip_longest(*iters))
答案 1 :(得分:7)
>>> zip(xrange(1, 7, 2),xrange(2, 8 , 2))
[(1, 2), (3, 4), (5, 6)]
如果这不是您想要的,请在您的问题帖子中提供更多示例。
答案 2 :(得分:6)
请参阅roundrobin
in the itertools
"Recipes" section。这是备用的更通用的版本。
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
答案 3 :(得分:2)
您可以像这样定义alternate
:
import itertools
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip(*iters)):
yield elt
print list(alternate(xrange(1, 7, 2), xrange(2, 8, 2)))
这留下了一个问题,即如果一个迭代器在另一个迭代器之前停止该怎么做。
如果您希望继续使用最长的迭代器,那么您可以使用itertools.izip_longest
代替itertools.izip
。
import itertools
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip_longest(*iters)):
yield elt
print list(alternate(xrange(1, 7, 2), xrange(2, 10, 2)))
这将产生收益
[1, 2, 3, 4, 5, 6, None, 8]
当迭代器xrange(1,7,2)引发StopIteration(没有更多元素)时,会产生None
。
如果你想跳过迭代器而不是屈服None
,你可以这样做:
Dummy=object()
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip_longest(*iters,fillvalue=Dummy)):
if elt is not Dummy:
yield elt
答案 4 :(得分:1)
如果长度相同,则可以像这样使用itertools.izip:
def alternate(*iters):
for row in itertools.izip(*iters):
for i in row:
yield i
答案 5 :(得分:1)
您的尝试有两个问题:
iters
将iter()
中的每个对象换行,因此会因list
之类的迭代而失败;和pass
StopIteration
您的生成器是一个无限循环。一些简单的代码可以解决这两个问题,并且仍然易于阅读和理解:
def alternate(*iters):
iters = [iter(i) for i in iters]
while True:
for i in iters:
yield next(i)
>>> list(alternate(range(1, 7, 2), range(2, 8, 2)))
[1, 2, 3, 4, 5, 6]