在 Python 中交错相同长度的多个列表

在 Python 中,有没有一种好的方法来交错两个长度相同的列表?

假设我得到了 [1,2,3][10,20,30],我想把它们转换成 [1,10,2,20,3,30]

58037 次浏览

在发布了这个问题之后,我意识到我可以简单地做以下几件事:

[val for pair in zip(l1, l2) for val in pair]

其中 l1l2是两个列表。


If there are N lists to interleave, then

lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]

我最喜欢 aix 的解决方案。下面是我认为应该在2.2中使用的另一种方法:

>>> x=range(3)
>>> x
[0, 1, 2]
>>> y=range(7,10)
>>> y
[7, 8, 9]
>>> sum(zip(x,y),[])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> sum(map(list,zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

还有一个办法:

>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]

以及:

>>> sum((list(i) for i in zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

选择:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> [y for x in map(None,l1,l2) for y in x if y is not None]
[1, 10, 2, 20, 3, 30]

This works because 地图 works on lists in parallel. It 都是一样的 under 2.2. By itself, with None as the called functions, map produces a list of tuples:

>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]

然后将元组列表展开。

当然,map的优势在于它可以适用于任意数量的列表,而且即使它们的长度不同,map也能正常工作:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> l3=[101,102,103,104]
>>> [y for x in map(None,l1,l2,l3) for y in x if y in not None]
[1, 10, 101, 2, 20, 102, 3, 30, 103, 104]

对于 Python > = 2.3,有 extended slice syntax:

>>> a = [0, 2, 4, 6, 8]
>>> b = [1, 3, 5, 7, 9]
>>> c = a + b
>>> c[::2] = a
>>> c[1::2] = b
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

使用 c = a + b行是一种简单的方法,可以创建一个长度恰好相同的新列表(在这个阶段,其内容并不重要)。接下来的两行执行交错 ab的实际工作: 第一行将 a的元素分配给 c的所有偶数索引; 第二行将 b的元素分配给 c的所有奇数索引。

我需要一种方法来做到这一点与不同大小的名单,接受的答案没有地址。

我的解决方案使用了一个生成器,它的使用看起来更好一些,因为它:

def interleave(l1, l2):
iter1 = iter(l1)
iter2 = iter(l2)
while True:
try:
if iter1 is not None:
yield next(iter1)
except StopIteration:
iter1 = None
try:
if iter2 is not None:
yield next(iter2)
except StopIteration:
iter2 = None
if iter1 is None and iter2 is None:
raise StopIteration()

它的用法:

>>> a = [1, 2, 3, 4, 5]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(interleave(a, b))
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g']
>>> list(interleave(b, a))
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']

给予

a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]

密码

假设列表的长度相同,您可以得到一个带有 itertools.chainzip的交错列表:

import itertools




list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]

替代品

itertools.zip_longest

More generally with unequal lists, use zip_longest (recommended):

[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]

许多列表可以安全地交替出现:

[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None]
# [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]

more_itertools < sup > +

一个包含 roundrobin迭代工具配方、 interleaveinterleave_longest的库。

import more_itertools




list(more_itertools.roundrobin(a, b))
# [1, 10, 2, 20, 3, 30]


list(more_itertools.interleave(a, b))
# [1, 10, 2, 20, 3, 30]


list(more_itertools.interleave_longest(a, c))
# [1, 100, 2, 200, 3, 300, 999]

yield from

最后,对于 Python 3中一些有趣的东西(尽管不推荐) :

list(filter(None, ((yield from x) for x in zip(a, b))))
# [1, 10, 2, 20, 3, 30]


list([(yield from x) for x in zip(a, b)])
# [1, 10, 2, 20, 3, 30]

+ 使用 pip install more_itertools安装

[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]

只要你没有想要保留的 None

要回答题目为“在 Python 中交叉多个相同长度的列表”的问题,我们可以推广@ekhumoro 的2列表答案。这显式地要求列表的长度相同,这与@NPE 的(优雅的)解决方案不同

import itertools


def interleave(lists):
"""Interleave a list of lists.


:param lists: List of lists; each inner length must be the same length.
:returns: interleaved single list
:rtype: list


"""
if len(set(len(_) for _ in lists)) > 1:
raise ValueError("Lists are not all the same length!")
joint = list(itertools.chain(*lists))
for l_idx, li in enumerate(lists):
joint[l_idx::len(lists)] = li
return joint

Examples:

>>> interleave([[0,2,4], [1, 3, 5]])
[0, 1, 2, 3, 4, 5]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]])
[0, 1, 10, 2, 3, 11, 4, 5, 12]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]])
[0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in interleave
ValueError: Lists are not all the same length!
>>> interleave([[0,2,4]])
[0, 2, 4]

来不及了,有很多好的答案,但我也想提供一个简单的解决方案,使用 extend()的方法:

list1 = [1, 2, 3]
list2 = [10, 20, 30]


new_list = []
for i in range(len(list1)):
new_list.extend([list1[i], list2[i]])
print(new_list)

产出:

[1, 10, 2, 20, 3, 30]

一个有趣的方法是使用 heapq.merge,并将最终列表中的位置作为键:

from heapq import merge
from itertools import count


a = [1,2,3]
b = [10,20,30]


counter = count()
res = list(merge(a, b, key=lambda x: next(counter)))
print(res)

输出

[1, 10, 2, 20, 3, 30]

对于多个列表,您可以直接解压缩它们:

from heapq import merge
from itertools import count


a = [1, 2, 3]
b = [10, 20, 30]
c = [11, 21, 31]


counter = count()
res = list(merge(*[a, b, c], key=lambda x: next(counter)))
print(res)

输出

[1, 10, 11, 2, 20, 21, 3, 30, 31]

这也是一种方法:

list1 = [1, 2, 3]
list2 = [10, 20, 30]


list(sum(zip(list1, list2), ()))

The idea is similar.

  1. zip the lists together. (using zip)
  2. 使用 sum (... ,())压平到元组
  3. 转换成一个列表