Modo Pythonic per restituire l'elenco di ogni ennesimo elemento in un elenco più grande


171

Supponiamo di avere un elenco di numeri da 0 a 1000. Esiste un modo pitonico / efficiente per produrre un elenco del primo e del successivo decimo elemento, ovvero [0, 10, 20, 30, ... ]?

Sì, posso farlo usando un ciclo for, ma mi chiedo se esiste un modo più ordinato per farlo, forse anche in una riga?

Risposte:


290
>>> lst = list(range(165))
>>> lst[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

Si noti che questo è circa 100 volte più veloce del loop e del controllo di un modulo per ciascun elemento:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop

4
Certo, la comprensione delle liste è più potente in generale. OTOH, la domanda pone un elenco esistente e, in tal caso, una sezione funziona perfettamente.
Ned Deily,

Ho commentato questo sotto nelle risposte alla comprensione dell'elenco. Fai attenzione con "if x% 10 == 0". Funziona solo con questo particolare esempio di elenco, ma se l'elenco di input è ad esempio l = range (0,1000,2) non verrà estratto ogni decimo elemento.
Andre Miller,

12
@Andre: molto vero. Quindi questo è un esempio di una funzione di linguaggio umile, l'operatore slice, che risulta in questo caso (1) per rendere più facile ottenere i risultati corretti; (2) si traduce in un'espressione più concisa; e (3) sembra essere 2 ordini di grandezza più veloce. (1) è di gran lunga la preoccupazione più importante, ovviamente, ma, grazie all'attenta progettazione e implementazione del linguaggio, ottieni tutti e tre al prezzo di 1. Bella domanda e risposte.
Ned Deily,

2
Il 0è ridondante in l[0::10]. l[::10]è più leggibile, meno confuso.
Konstantin Schubert,

Sono sorpreso dal confronto delle prestazioni 0,5 secondi per la comprensione dell'elenco e 0,4 per la sezione dell'elenco. Sembra molto lento, perché la suddivisione in liste richiede 100 mila cicli per un elenco di dimensioni 1 mila !?
Damo,

58
  1. source_list[::10] è il più ovvio, ma questo non funziona per nessun iterabile e non è efficiente in termini di memoria per elenchi di grandi dimensioni.
  2. itertools.islice(source_sequence, 0, None, 10) funziona per qualsiasi iterabile ed è efficiente in termini di memoria, ma probabilmente non è la soluzione più veloce per grandi elenchi e grandi passi.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))

1
+1 Migliore risposta, IMO. Tutte e tre le proposte sono una soluzione generale (cioè prendere l'elenco delle fonti come un dato). La soluzione del generatore (3.) è piacevole in quanto filtra sull'indice dell'elenco delle fonti. Probabilmente è efficiente in termini di memoria come 2. Sia gli indici che l'elenco dei risultati sono generatori e quindi costruiti pigramente, che è probabilmente anche il più veloce se non hai bisogno dell'elenco dei risultati in un singolo blocco. Solo se la lista delle fonti potesse essere un generatore andrei con il "item, i in enumerate (l)" idioma di Paul, poiché non c'è len () di un generatore. A proposito, che tipo di iterabile non funzionerebbe con 1.? Generatori ?!
ThomasH,

Iterable = oggetto con il metodo __iter __ () che restituisce iteratore (oggetto con il metodo next ())
Denis Otkidach,


19

Dal manuale: s[i:j:k] slice of s from i to j with step k

li = range(100)
sub = li[0::10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

13
newlist = oldlist[::10]

Questo seleziona ogni decimo elemento dell'elenco.


4

Perché non usare semplicemente un parametro step della funzione range anche per ottenere:

l = range(0, 1000, 10)

Per confronto, sulla mia macchina:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop

3
@SilentGhost: è vero, ma poiché si tratta di una domanda per principianti, la funzione range potrebbe essere ciò che vogliono veramente fare, quindi penso che sia una risposta valida. (Anche se il limite superiore dovrebbe essere 1001, non 1000)
Scott Griffiths,

2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

1
perché hai la clausola if quando range (0, 1001, 10) prende già solo ogni 10 ° elemento?
Autoplectic,

4
Stesso commento qui, questo non risolve il problema più generale di "Modo Pythonic per restituire l'elenco di ogni ennesimo elemento in un elenco più ampio", la soluzione dipende dal fatto che i valori dell'elenco di esempio sono compresi tra 0 e 1000 e estrae solo gli elementi fuori dall'elenco che ha un valore divisibile per 10 anziché ogni decimo elemento.
Andre Miller,

1
Bene, l'OP scrive: "abbiamo un elenco di numeri da zero a 1000". Quindi non ha bisogno di una soluzione generale.

1
Scrive 'Say we have ..' che implica che è solo un esempio. Se volesse davvero ogni decimo numero da un elenco da zero a 1000, la risposta sarebbe range (0,1001,10) o qualcosa di simile.
Andre Miller,

1

Ecco una migliore implementazione di una comprensione dell'elenco "ogni decimo elemento", che non utilizza i contenuti dell'elenco come parte del test di appartenenza:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

Ma questo è ancora molto più lento del semplice utilizzo dell'elenco delle sezioni.


-9

Le comprensioni dell'elenco sono fatte esattamente per questo:

smaller_list = [x for x in range(100001) if x % 10 == 0]

Puoi ottenere maggiori informazioni a riguardo nella documentazione ufficiale di Python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions


Il limite superiore dovrebbe essere 1000, non 10000. La soluzione non include il limite superiore di 1000 poiché l'intervallo si arresta a 999. +1 per il collegamento alla comprensione dell'elenco.

19
Questo in realtà non estrae ogni decimo oggetto, estrae ogni oggetto che ha un valore divisibile per 10. In questo esempio particolare è la stessa cosa, ma potrebbe non esserlo.
Andre Miller,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.