¿Una forma pythonica de combinar dos listas de manera alterna?

Tengo dos listas, la primera de las cuales se garantiza que contiene exactamente un elemento más que la segunda . Me gustaría saber la forma más Pythonic de crear una nueva lista cuyos valores de índice par provienen de la primera lista y cuyos valores de índice impar provienen de la segunda lista.

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

Esto funciona, pero no es bonito:

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

¿De qué otra manera se puede lograr esto? ¿Cuál es el enfoque más pythonico?

Aquí hay una forma de hacerlo cortando:

 >>> 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'] 

Hay una receta para esto en la documentación de itertools :

 from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> ADEBFC" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).next for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending)) 

Esto debería hacer lo que quieras:

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

Creo que esta es la forma más pythonica de hacerlo.

Sin itertools y suponiendo que l1 es 1 elemento más largo que l2:

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

Usando itertools y asumiendo que las listas no contienen ninguna:

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

Sé que las preguntas se refieren a dos listas con una que tiene un elemento más que la otra, pero pensé que pondría esto para otras personas que puedan encontrar esta pregunta.

Aquí está la solución de Duncan adaptada para trabajar con dos listas de diferentes tamaños.

 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 

Esto produce:

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

Aquí hay un forro que lo hace:

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

Este se basa en la contribución de Carlos Valiente anterior con una opción para alternar grupos de múltiples elementos y asegurarse de que todos los elementos estén presentes en la salida:

 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)] 

Esto entrelazará las listas A y B por grupos de 3 valores cada uno:

 ['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] 

Mi toma:

 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)] 

Aquí hay una sola línea que usa listas de comprensión, sin otras bibliotecas:

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

Aquí hay otro enfoque, si permite la alteración de su lista inicial1 por efecto secundario:

 [list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))] 
 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 

Podría ser un poco tarde comprar otra python de una sola línea. Esto funciona cuando las dos listas tienen un tamaño igual o desigual. Una cosa que no vale nada es que modificará a y b. Si es un problema, necesita utilizar otras soluciones.

 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'] 

Se detiene en el más corto:

 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])) 

Claro, resolver el siguiente / __ next__ método puede ser más rápido.

Esto es desagradable pero funciona sin importar el tamaño de las listas:

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

Múltiples frases de una línea inspiradas en respuestas a otra pregunta :

 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] 

¿Qué tal numpy? Funciona también con cuerdas:

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

Resultado:

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

Yo haría lo simple:

 chain.from_iterable( izip( list1, list2 ) ) 

Se creará un iterador sin crear ninguna necesidad de almacenamiento adicional.

Soy demasiado viejo para tener una lista de comprensión, así que:

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