Python有内置函数用于混合生成器/序列吗?

17 投票
3 回答
5337 浏览
提问于 2025-04-17 09:44

我注意到,itertools 似乎没有一个可以将多个可迭代对象的元素交错排列的函数(也就是和合并不同)。

def leaf(*args): return (it.next() for it in cycle(imap(chain,args)))
tuple(leaf(['Johann', 'Sebastian', 'Bach'], repeat(' '))) => ('Johann', ' ', 'Sebastian', ' ', 'Bach', ' ')

(编辑)我之所以这么问,是因为我想避免不必要的合并和扁平化操作。

显然,leaf 的定义很简单,但如果有一个预定义的函数可以做到同样的事情,我更愿意使用那个,或者是一个非常清晰的生成器表达式。 在 itertools 或其他一些知名库中,有这样的内置函数吗,或者有什么合适的表达方式?

编辑 2:使用 functional 包,可以得到一个更简洁的定义:

from itertools import *
from functional import *

compose_mult = partial(reduce, compose)
leaf = compose_mult((partial(imap, next), cycle, partial(imap, chain), lambda *args: args))

3 个回答

2

这个自定义函数会把多个迭代器交替组合在一起,并一直进行,直到所有的迭代器都用完为止。

def interleave_iterators(*iterators):
    finished = [False for x in range(len(iterators))]
    stop_cond = functools.reduce(lambda x,y:not x or not y,finished)
    while stop_cond:
        for i,it in enumerate(iterators):
            try:
                yield next(it)
            except StopIteration:
                finished[i] = True
        stop_cond = functools.reduce(lambda x,y:not x or not y,finished)

比如说

it1 = iter([2,4,6,8])
it2 = iter([1,3,5,7,9,11,13])
for x in interleave_iterators(it1,it2): # prints 2 1 4 3 6 5 8 7 9 11 13
    print(str(x),end=" ")
17

你可以使用内置的 zipitertools.chain.from_iterable 来把结果变得更简单易读:

>>> import itertools
>>> list(zip(['Johann', 'Sebastian', 'Bach'], itertools.repeat(' ')))
[('Johann', ' '), ('Sebastian', ' '), ('Bach', ' ')]
>>> list(itertools.chain.from_iterable(_))
['Johann', ' ', 'Sebastian', ' ', 'Bach', ' ']

注意,我使用 list 只是为了让输出看起来更好。使用标准的 itertools,leaf 的其他实现方式可以是:

leaf = lambda *a: itertools.chain.from_iterable(itertools.izip(*a)) # Python 2.x
leaf = lambda *a: itertools.chain.from_iterable(zip(*a))            # Python 3.x
8

itertools库里的roundrobin()方法本来是我最想用的,不过在你这个具体的例子里,它会产生一个无限的序列,因为它是根据最长的可迭代对象来停止,而不是最短的。当然,这个问题很容易解决。也许换个思路看看这个方法会更好?

撰写回答