我遇到了执行n个并发事件的问题,这些事件都将迭代器返回到他们获取的结果.但是,有一个可选的限制参数,基本上说,要整合所有迭代器并返回最多限制结果.
因此,例如:我在8个线程上执行2,000个url请求但只想要前100个结果,但不是所有100个来自同一个潜在线程.
因此,解开:
import itertools
def unravel(*iterables, with_limit = None):
make_iter = {a:iter(i) for a,i in enumerate(iterables)}
if not isinstance(with_limit, int):
with_limit = -1
resize = False
while True:
for iid, take_from in make_iter.items():
if with_limit == 0:
raise StopIteration
try:
yield next(take_from)
except StopIteration:
resize = iid
else:
with_limit -= 1
if resize:
resize = False
if len(make_iter.keys()) > 1:
make_iter.pop(resize)
else: raise StopIteration
用法:
>>> a = [1,2,3,4,5]
>>> b = [6,7,8,9,10]
>>> c = [1,3,5,7]
>>> d = [2,4,6,8]
>>>
>>> print([e for e in unravel(c, d)])
[1, 2, 3, 4, 5, 6, 7, 8]
>>> print([e for e in unravel(c, d, with_limit = 3)])
[1, 2, 3]
>>> print([e for e in unravel(a, b, with_limit = 6)])
[1, 6, 2, 7, 3, 8]
>>> print([e for e in unravel(a, b, with_limit = 100)])
[1, 6, 2, 7, 3, 8, 4, 9, 5, 10]
这样的事情是否已经存在,或者这是一个不错的实现?
谢谢
编辑,工作修复
受到@abernert建议的启发,这就是我的选择.谢谢大家!
def unravel(*iterables, limit = None):
yield from itertools.islice(
filter(None,
itertools.chain.from_iterable(
itertools.zip_longest(
*iterables
)
)
), limit)
>>> a = [x for x in range(10)]
>>> b = [x for x in range(5)]
>>> c = [x for x in range(0, 20, 2)]
>>> d = [x for x in range(1, 30, 2)]
>>>
>>> print(list(unravel(a, b)))
[1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9]
>>> print(list(unravel(a, b, limit = 3)))
[1, 1, 2]
>>> print(list(unravel(a, b, c, d, limit = 20)))
[1, 1, 1, 2, 3, 2, 2, 4, 5, 3, 3, 6, 7, 4, 4, 8, 9, 5, 10, 11]
最佳答案 你在这里做的几乎就是拉链.
你想要一个可迭代的平面,而不是可迭代的子迭代,而是链修复.
并且您只想获取前N个值,但是islice修复了这个值.
所以,如果长度都相等:
>>> list(chain.from_iterable(zip(a, b)))
[1, 6, 2, 7, 3, 8, 4, 9, 5, 10]
>>> list(islice(chain.from_iterable(zip(a, b)), 7))
[1, 6, 2, 7, 3, 8, 4]
但是如果长度不相等,那么只要第一个可迭代完成就会停止,这是你不想要的.而stdlib中唯一的替代方法是zip_longest,它使用None填充缺失值.
你可以很容易地编写一个zip_longest_skipping(这实际上是Peter的答案中的round_robin),但你也可以只是zip_longest并过滤掉结果:
>>> list(filter(None, chain.from_iterable(zip_longest(a, b, c, d))))
[1, 6, 1, 2, 2, 7, 3, 4, 3, 8, 5, 6, 4, 9, 7, 8, 5, 10]
(显然如果你的值都是字符串或None都不行,但是当它们都是正整数时它工作正常……处理“或无”情况时,执行sentinel = object(),将其传递给zip_longest,然后在x上过滤不是哨兵.)