2010-04-13 14 views
36

Mi sembra di passare molto tempo a scrivere codice in Python, ma non abbastanza tempo per creare codice Pythonic. Recentemente mi sono imbattuto in un piccolo problema divertente che ho pensato potesse avere una soluzione facile e idiomatica. Parafrasando l'originale, avevo bisogno di raccogliere ogni coppia sequenziale in una lista. Ad esempio, dato l'elenco [1,2,3,4,5,6], volevo calcolare [(1,2),(3,4),(5,6)].Python "Ogni altro elemento" Idiom

All'epoca ho trovato una soluzione rapida che sembrava tradurre Java. Rivisitare la questione, il meglio che potevo fare era

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

che ha l'effetto collaterale di tirare fuori l'ultimo numero nel caso in cui la lunghezza non è nemmeno.

C'è un approccio più idiomatico che mi manca, o è questo il migliore che otterrò?

+0

correlate "Qual è il più‘divinatorio’modo per iterare su una lista in pezzi?" http://stackoverflow.com/questions/434287/what-is-the-most-pythonic-way-to-iterate-over-a-list-in-chunks – jfs

risposta

78

Questo farà un po 'più ordinatamente:

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

(ma è senza dubbio meno leggibile se non hai familiarità con la funzione di "passo" del range).

Come il codice, elimina l'ultimo valore in cui si dispone di un numero dispari di valori.

+3

ti piace la funzione 'zip'. –

+2

Puoi fare un 'data + = [None]' prima di fare quanto sopra per gestire lo scenario di numero dispari di oggetti. – badp

+1

'some_list + = [foo]' è scritto 'some_list.append (foo)'. –

8

ne dite di usare la funzione di passaggio di range():

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

L'uno spesso citato è:

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

awsome, ma si legge come perl;) –

+0

Vero, ma è anche la soluzione più efficiente presentata finora, penso. Ci proverò, brb. – jemfinch

+1

Sì, è più veloce della soluzione di RichieHindle di circa il 10%, poiché non richiede alcuna allocazione di memoria e solo una iterazione sull'elenco di input. – jemfinch

4

provare questo

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

Quindi,

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

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

Io di solito copiare la ricetta grouper dalla documentazione itertools nel mio codice per questo.

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 per aver menzionato il manuale F :) – tzot

3

Se non si vuole perdere elementi se il loro numero in elenco non è nemmeno provare questo:

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

La cosa giusta è probabilmente di non calcolare le liste, ma per scrivere un iterator- > funzione iteratore. Questo è più generico - funziona su ogni iterable e se vuoi "congelarlo" in una lista, puoi usare la funzione "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) 

Mi sorprende il modulo itertools non dispone già di una funzione per questo - forse una futura revisione. Fino ad allora, sentiti libero di usare la versione sopra :)

+0

Cos'è questo tipo di rendimento? – LondonRob

4

toolz è una libreria ben costruita con molte sottigliezze di programmazione funzionale trascurate in itertools.partition risolve questo (con un'opzione per riempire l'ultima voce per liste di lunghezza dispari)

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