在itertools
中有chain
,它将多个生成器组合在一起,实质上是对它们进行深度优先迭代,即chain.from_iterable(['ABC', '123'])
得到A,B,C ,1,2,3。但是,没有广度优先版本,或者我错过了什么?当然有izip_longest
,但是对于大量的生成器来说,这感觉很尴尬,因为元组会非常长并且可能非常稀疏。
我想出了以下内容:
def chain_bfs(*generators):
generators = list(generators)
while generators:
g = generators.pop(0)
try:
yield g.next()
except StopIteration:
pass
else:
generators.append(g)
对我来说感觉有点啰嗦,我错过了更多的Pythonic方法吗?这个函数是否适合包含在itertools
?
答案 0 :(得分:7)
您可以使用collections.deque()
来旋转迭代器;旋转双端队列效率更高。我也把它称为链式拉链,而不是第一链#',因此:
from collections import deque
def chained_zip(*iterables):
iterables = deque(map(iter, iterables))
while iterables:
try:
yield next(iterables[0])
except StopIteration:
iterables.popleft()
else:
iterables.rotate(-1)
演示:
>>> list(chained_zip('ABC', '123'))
['A', '1', 'B', '2', 'C', '3']
>>> list(chained_zip('AB', '1234'))
['A', '1', 'B', '2', '3', '4']
还有roundrobin()
recipe in the documentation使用itertools.cycle()
function:
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))
答案 1 :(得分:1)
不确定你是否仍然认为这太“冗长”......
def chain_bfs2(*generators):
generators = map(iter, generators)
while generators:
for i, generator in enumerate(generators):
try:
yield generator.next()
except StopIteration:
del generators[i]
print list(chain_bfs2('AB', '123')) # ['A', '1', 'B', '2', '3']