是否有一个内置函数,工作类似zip(),但将填充结果,使结果列表的长度是最长的输入而不是最短的输入的长度?

>>> a = ['a1']
>>> b = ['b1', 'b2', 'b3']
>>> c = ['c1', 'c2']

>>> zip(a, b, c)
[('a1', 'b1', 'c1')]

>>> What command goes here?
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]

当前回答

只是使用迭代器,没什么特别的。

def zip_longest(*iterables):
    items = 0
    for iterable in iterables:
        items = max(items, len(iterable))

    iters = [iter(iterable) for iterable in iterables]
    while items:
        yield (*[next(i, None) for i in iters],)
        items -= 1

其他回答

在Python 3中,您可以使用itertools.zip_longest

>>> list(itertools.zip_longest(a, b, c))
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]

你可以使用fillvalue参数填充不同于None的值:

>>> list(itertools.zip_longest(a, b, c, fillvalue='foo'))
[('a1', 'b1', 'c1'), ('foo', 'b2', 'c2'), ('foo', 'b3', 'foo')]

在Python 2中,你可以使用itertools。izip_longest (Python 2.6+),或者可以使用map和None。这是map的一个鲜为人知的特性(但是map在Python 3中改变了。x,所以这只适用于Python 2.x)。

>>> map(None, a, b, c)
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]

non itertools Python 3 解决方案:

def zip_longest(*lists):
    def g(l):
        for item in l:
            yield item
        while True:
            yield None
    gens = [g(l) for l in lists]    
    for _ in range(max(map(len, lists))):
        yield tuple(next(g) for g in gens)

对于Python 2.6x,请使用itertools模块的izip_longest。

对于Python 3,使用zip_longest代替(没有i前导)。

>>> list(itertools.izip_longest(a, b, c))
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]

只是使用迭代器,没什么特别的。

def zip_longest(*iterables):
    items = 0
    for iterable in iterables:
        items = max(items, len(iterable))

    iters = [iter(iterable) for iterable in iterables]
    while items:
        yield (*[next(i, None) for i in iters],)
        items -= 1

为了补充已经给出的答案,以下内容适用于任何可迭代对象,不使用itertools,回答@ProdIssue的问题:

def zip_longest(*iterables, default_value):
    iterators = tuple(iter(i) for i in iterables)
    sentinel = object()
    while True:
        new = tuple(next(i, sentinel) for i in iterators)
        if all(n is sentinel for n in new):
            return
        yield tuple(default_value if n is sentinel else n for n in new)

需要使用sentinel,以便产生default_value的迭代器不会被错误地标识为空。