交错交错多个迭代,同时在python中保留它们的顺序

srg*_*erg 9 python

受此早期堆栈溢出问题的启发,我一直在考虑如何在python中随机交错迭代,同时保留每个迭代中元素的顺序.例如:

>>> def interleave(*iterables):
...     "Return the source iterables randomly interleaved"
...     <insert magic here>
>>> interleave(xrange(1, 5), xrange(5, 10), xrange(10, 15))
[1, 5, 10, 11, 2, 6, 3, 12, 4, 13, 7, 14, 8, 9]
Run Code Online (Sandbox Code Playgroud)

原始问题要求随机交错两个列表a和b,并且接受的解决方案是:

>>> c = [x.pop(0) for x in random.sample([a]*len(a) + [b]*len(b), len(a)+len(b))]
Run Code Online (Sandbox Code Playgroud)

然而,这种解决方案适用于只有两个列表(尽管它可以很容易地扩展),并依赖于一个事实,即a和b是列表,以便pop()len()可以叫上他们,这意味着它不能与iterables使用.它还有清空源列表a和b的不幸副作用.

给出原始问题的替代答案会获取源列表的副本以避免修改它们,但这对我来说效率低下,特别是如果源列表相当大.备用答案也可以使用,len()因此不能仅用于迭代.

我编写了自己的解决方案,适用于任意数量的输入列表,不会修改它们:

def interleave(*args):
    iters = [i for i, b in ((iter(a), a) for a in args) for _ in xrange(len(b))]
    random.shuffle(iters)
    return map(next, iters)
Run Code Online (Sandbox Code Playgroud)

但是这个解决方案还依赖于源参数是列表,以便len()可以在它们上使用.

那么,有没有一种有效的方法可以在python中随机交错迭代,保留元素的原始顺序,这不需要提前知道迭代的长度,也不需要复制迭代?

编辑:请注意,与原始问题一样,我不需要随机化是公平的.

NPE*_*NPE 10

以下是使用生成器执行此操作的一种方法:

import random

def interleave(*args):
  iters = map(iter, args)
  while iters:
    it = random.choice(iters)
    try:
      yield next(it)
    except StopIteration:
      iters.remove(it)

print list(interleave(xrange(1, 5), xrange(5, 10), xrange(10, 15)))
Run Code Online (Sandbox Code Playgroud)