Python 的方式来组合(交织,交织,交织)两个列表在一个交替的方式?

我有两个名单,其中第一个保证比第二个包含正好多一个项。我想知道创建一个新列表的最 Python 的方法,它的偶数索引值来自第一个列表,奇数索引值来自第二个列表。

# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']


# desired output
['f', 'hello', 'o', 'world', 'o']

这种做法有效,但并不好看:

list3 = []
while True:
try:
list3.append(list1.pop(0))
list3.append(list2.pop(0))
except IndexError:
break

How else can this be achieved? What's the most Pythonic approach?


如果您需要处理 不匹配的长度的列表(例如,第二个列表更长,或者第一个列表比第二个列表具有多于一个元素) ,这里的一些解决方案可以起作用,而其他解决方案则需要进行调整。有关更具体的答案,请参见 如何交错两个不同长度的列表?将多余的元素保留在末尾,或者参见 如何在 python 中优雅地交错两个长度不均匀的列表?尝试均匀地散布元素。

110718 次浏览

itertools documentation(注意: 对于 Python 3)中有一个这方面的配方:

from itertools import cycle, islice


def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
for next in nexts:
yield next()
except StopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -= 1
nexts = cycle(islice(nexts, num_active))

在 Python2中,这应该可以满足您的需要:

>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']

这里有一种切片的方法:

>>> list1 = ['f', 'o', 'o']
>>> list2 = ['hello', 'world']
>>> result = [None]*(len(list1)+len(list2))
>>> result[::2] = list1
>>> result[1::2] = list2
>>> result
['f', 'hello', 'o', 'world', 'o']

我会做简单的:

chain.from_iterable( izip( list1, list2 ) )

它将提供一个迭代器,而不需要创建任何额外的存储需求。

我年纪太大了,不适合理解列表,所以:

import operator
list3 = reduce(operator.add, zip(list1, list2))

Here's a one liner that does it:

list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]

下面是使用列表理解的一行代码,包括其他库:

list3 = [sub[i] for i in range(len(list2)) for sub in [list1, list2]] + [list1[-1]]

这里有另一种方法,如果你允许改变你的初始列表1的副作用:

[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]

My take:

a = "hlowrd"
b = "el ol"


def func(xs, ys):
ys = iter(ys)
for x in xs:
yield x
yield ys.next()


print [x for x in func(a, b)]

Stops on the shortest:

def interlace(*iters, next = next) -> collections.Iterable:
"""
interlace(i1, i2, ..., in) -> (
i1-0, i2-0, ..., in-0,
i1-1, i2-1, ..., in-1,
.
.
.
i1-n, i2-n, ..., in-n,
)
"""
return map(next, cycle([iter(x) for x in iters]))

当然,解析下一个/_ _ next _ _ 方法可能更快。

def combine(list1, list2):
lst = []
len1 = len(list1)
len2 = len(list2)


for index in range( max(len1, len2) ):
if index+1 <= len1:
lst += [list1[index]]


if index+1 <= len2:
lst += [list2[index]]


return lst

如果没有 itertools 并且假设 l1是一个比 l2长的项:

>>> sum(zip(l1, l2+[0]), ())[:-1]
('f', 'hello', 'o', 'world', 'o')

在 python 2中,使用 itertools 并假设列表不包含 Nothing:

>>> filter(None, sum(itertools.izip_longest(l1, l2), ()))
('f', 'hello', 'o', 'world', 'o')

This one is based on Carlos Valiente's contribution above 可以选择将多个项目交替分组,并确保所有项目都出现在输出中:

A=["a","b","c","d"]
B=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]


def cyclemix(xs, ys, n=1):
for p in range(0,int((len(ys)+len(xs))/n)):
for g in range(0,min(len(ys),n)):
yield ys[0]
ys.append(ys.pop(0))
for g in range(0,min(len(xs),n)):
yield xs[0]
xs.append(xs.pop(0))


print [x for x in cyclemix(A, B, 3)]

这将交织列表 A 和 B,每组3个值:

['a', 'b', 'c', 1, 2, 3, 'd', 'a', 'b', 4, 5, 6, 'c', 'd', 'a', 7, 8, 9, 'b', 'c', 'd', 10, 11, 12, 'a', 'b', 'c', 13, 14, 15]

我知道这些问题会询问两个列表,其中一个列表中的一个项目比另一个多,但我想我会把这个问题提供给其他可能发现这个问题的人。

这里是 邓肯的解决方案适应工作与两个不同大小的列表。

list1 = ['f', 'o', 'o', 'b', 'a', 'r']
list2 = ['hello', 'world']
num = min(len(list1), len(list2))
result = [None]*(num*2)
result[::2] = list1[:num]
result[1::2] = list2[:num]
result.extend(list1[num:])
result.extend(list2[num:])
result

产出:

['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r']

这种做法令人不快,但无论名单的大小如何,它都是有效的:

list3 = [
element for element in
list(itertools.chain.from_iterable([
val for val in itertools.izip_longest(list1, list2)
]))
if element != None
]
import itertools
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]

我觉得这是最简单的方法了。

另一个问题的答案启发的多个俏皮话:

import itertools


list(itertools.chain.from_iterable(itertools.izip_longest(list1, list2, fillvalue=object)))[:-1]


[i for l in itertools.izip_longest(list1, list2, fillvalue=object) for i in l if i is not object]


[item for sublist in map(None, list1, list2) for item in sublist][:-1]

可能有点晚了,再买一个蟒蛇俏皮话。当两个列表的大小相等或不相等时,这种方法可以工作。有一点毫无价值,那就是它会修改 a 和 b。如果这是一个问题,您需要使用其他解决方案。

a = ['f', 'o', 'o']
b = ['hello', 'world']
sum([[a.pop(0), b.pop(0)] for i in range(min(len(a), len(b)))],[])+a+b
['f', 'hello', 'o', 'world', 'o']

Numpy 怎么样? 它也适用于字符串:

import numpy as np


np.array([[a,b] for a,b in zip([1,2,3],[2,3,4,5,6])]).ravel()

Result:

array([1, 2, 2, 3, 3, 4])

函数式和不可变方式的替代方案(Python 3) :

from itertools import zip_longest
from functools import reduce


reduce(lambda lst, zipped: [*lst, *zipped] if zipped[1] != None else [*lst, zipped[0]], zip_longest(list1, list2),[])
from itertools import chain
list(chain(*zip('abc', 'def')))  # Note: this only works for lists of equal length
['a', 'd', 'b', 'e', 'c', 'f']

如果两个列表的长度相同,则可以:

[x for y in zip(list1, list2) for x in y]

由于第一个列表还有一个元素,因此可以事后添加它:

[x for y in zip(list1, list2) for x in y] + [list1[-1]]

itertools.zip_longest 返回一个元组对迭代器,其中一个列表中的任何缺失元素都由 fillvalue=None替换(传递 fillvalue=object使您可以使用 None作为值)。如果你选择这些对,然后用一个列表内涵过滤它们,你会得到:

>>> from itertools import zip_longest
>>> def merge(a, b):
...     return [
...         x for y in zip_longest(a, b, fillvalue=object)
...         for x in y if x is not object
...     ]
...
>>> merge("abc", "defgh")
['a', 'd', 'b', 'e', 'c', 'f', 'g', 'h']
>>> merge([0, 1, 2], [4])
[0, 4, 1, 2]
>>> merge([0, 1, 2], [4, 5, 6, 7, 8])
[0, 4, 1, 5, 2, 6, 7, 8]

推广到任意的迭代:

>>> def merge(*its):
...     return [
...         x for y in zip_longest(*its, fillvalue=object)
...         for x in y if x is not object
...     ]
...
>>> merge("abc", "lmn1234", "xyz9", [None])
['a', 'l', 'x', None, 'b', 'm', 'y', 'c', 'n', 'z', '1', '9', '2', '3', '4']
>>> merge(*["abc", "x"]) # unpack an iterable
['a', 'x', 'b', 'c']

最后,返回一个生成器而不是列表内涵:

>>> def merge(*its):
...     return (
...         x for y in zip_longest(*its, fillvalue=object)
...         for x in y if x is not object
...     )
...
>>> merge([1], [], [2, 3, 4])
<generator object merge.<locals>.<genexpr> at 0x000001996B466740>
>>> next(merge([1], [], [2, 3, 4]))
1
>>> list(merge([1], [], [2, 3, 4]))
[1, 2, 3, 4]

如果你对其他软件包没问题,你可以试试 more_itertools.roundrobin:

>>> list(roundrobin('ABC', 'D', 'EF'))
['A', 'D', 'E', 'B', 'F', 'C']

using for loop also we can achive this easily:

list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']
list3 = []


for i in range(len(list1)):
#print(list3)
list3.append(list1[i])
if i < len(list2):
list3.append(list2[i])
        

print(list3)

output :

['f', 'hello', 'o', 'world', 'o']

通过使用 list comprehension可以进一步减少这种情况,但是为了理解这个循环可以使用。

Obviously late to the party, but here's a concise one for equal-length lists:

output = [e for sub in zip(list1,list2) for e in sub]

它也适用于任意数目的等长列表:

output = [e for sub in zip(list1,list2,list3) for e in sub]

etc.

我的方法如下:

from itertools import chain, zip_longest


def intersperse(*iterators):
# A random object not occurring in the iterators
filler = object()


r = (x for x in chain.from_iterable(zip_longest(*iterators, fillvalue=filler)) if x is not filler)


return r


list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']


print(list(intersperse(list1, list2)))

它适用于任意数量的迭代器并产生一个迭代器,因此我在打印行中应用了 list()