2010-04-13 16 views
36

Siento que paso mucho tiempo escribiendo código en Python, pero no tengo suficiente tiempo para crear código Pythonic. Recientemente me encontré con un pequeño problema divertido que pensé que podría tener una solución fácil e idiomática. Parafraseando el original, necesitaba recolectar cada par secuencial en una lista. Por ejemplo, dada la lista [1,2,3,4,5,6], quería calcular [(1,2),(3,4),(5,6)].Python "Todos los demás elementos" Idiom

Se me ocurrió una solución rápida en ese momento que parecía traducida como Java. Revisando la pregunta, lo mejor que podía hacer era

l = [1,2,3,4,5,6] 
[(l[2*x],l[2*x+1]) for x in range(len(l)/2)] 

que tiene el efecto secundario de sacudir hacia fuera el último número en el caso de que la longitud no es uniforme.

¿Hay un enfoque más idiomático que me falta, o es esto lo mejor que voy a obtener?

+0

relacionados con "¿Cuál es el más‘Pythonic’manera de iterar sobre una lista en trozos?" http://stackoverflow.com/questions/434287/what-is-the-most-pythonic-way-to-iterate-over-a-list-in-chunks – jfs

Respuesta

78

Esto lo hará un poco más nítidamente:

>>> data = [1,2,3,4,5,6] 
>>> zip(data[0::2], data[1::2]) 
[(1, 2), (3, 4), (5, 6)] 

(pero es sin duda menos legible si no está familiarizado con la función de "paso" de rangos).

Como su código, descarta el último valor donde tiene un número impar de valores.

+3

adora la función 'zip'. –

+2

Puede hacer un 'data + = [None]' antes de hacer lo anterior para manejar el escenario de número impar de elementos. – badp

+1

'some_list + = [foo]' está escrito 'some_list.append (foo)'. –

8

¿Cómo sobre el uso de la función de paso de range():

[(l[n],l[n+1]) for n in range(0,len(l),2)] 
48

El uno, citada con frecuencia es:

zip(*[iter(l)] * 2) 
+8

impresionante, pero se lee como perl; -) –

+0

Es cierto, pero también es la solución más eficiente presentada hasta ahora, creo. Voy a probar, brb. – jemfinch

+1

Sí, es más rápido que la solución de RichieHindle en aproximadamente un 10%, ya que no requiere ninguna asignación de memoria y solo una iteración sobre la lista de entrada. – jemfinch

4

probar esto

def pairs(l, n): 
    return zip(*[l[i::n] for i in range(n)]) 

Así,

pairs([1, 2, 3, 4], 2) da

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

generalmente copio el grouper receta de la documentación itertools en mi código para esto.

def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 
+1

+1 por mencionar el Manual F :) – tzot

3

Si no quiere perder los elementos si su número en la lista ni siquiera se intente esto:

>>> l = [1, 2, 3, 4, 5] 
>>> [(l[i], l[i+1] if i+1 < len(l) else None) for i in range(0, len(l), 2)] 
[(1, 2), (3, 4), (5, None)] 
4

Lo correcto es probable que no computar las listas, pero escribir un iterator- > función de iterador. Esto es más genérico: funciona en todos los iterables, y si desea "congelarlo" en una lista, puede usar la función "list()".

def groupElements(iterable, n): 
    # For your case, you can hardcode n=2, but I wanted the general case here. 
    # Also, you do not specify what to do if the 
    # length of the list is not divisible by 2 
    # I chose here to drop such elements 
    source = iter(iterable) 
    while True: 
     l = [] 
     for i in range(n): 
      l.append(source.next()) 
     yield tuple(l) 

Me sorprende que el módulo de itertools aún no tiene una función para la que - tal vez una futura revisión. Hasta entonces, no dude en utilizar la versión anterior :)

+0

¿Qué es esto como rendimiento? – LondonRob

4

toolz es una biblioteca bien construida con muchos detalles funcionales de programación que se pasan por alto en itertools.partition resuelve este (con una opción para rellenar la última entrada para las listas de longitud impar)

>>> list(toolz.partition(2, [1,2,3,4,5,6])) 
[(1, 2), (3, 4), (5, 6)] 
Cuestiones relacionadas