Seleziona esplicitamente gli elementi da un elenco o da una tupla


120

Ho il seguente elenco di Python (può anche essere una tupla):

myList = ['foo', 'bar', 'baz', 'quux']

posso dire

>>> myList[0:3]
['foo', 'bar', 'baz']
>>> myList[::2]
['foo', 'baz']
>>> myList[1::2]
['bar', 'quux']

Come faccio a selezionare esplicitamente gli elementi i cui indici non hanno modelli specifici? Ad esempio, voglio selezionare [0,2,3]. O da un elenco molto grande di 1000 elementi, voglio selezionare [87, 342, 217, 998, 500]. C'è qualche sintassi Python che lo fa? Qualcosa che assomiglia a:

>>> myBigList[87, 342, 217, 998, 500]

1
Questo sembra essere un duplicato. L'altra domanda ha più voti positivi, ma sembra che abbia una risposta migliore con i tempi.
AnnanFay

Risposte:


149
list( myBigList[i] for i in [87, 342, 217, 998, 500] )

Ho confrontato le risposte con python 2.5.2:

  • 19.7 usec: [ myBigList[i] for i in [87, 342, 217, 998, 500] ]

  • 20.6 usec: map(myBigList.__getitem__, (87, 342, 217, 998, 500))

  • 22,7 usec: itemgetter(87, 342, 217, 998, 500)(myBigList)

  • 24.6 usec: list( myBigList[i] for i in [87, 342, 217, 998, 500] )

Nota che in Python 3, il primo è stato modificato per essere lo stesso del quarto.


Un'altra opzione sarebbe quella di iniziare con un numpy.arrayche consente l'indicizzazione tramite un elenco o un numpy.array:

>>> import numpy
>>> myBigList = numpy.array(range(1000))
>>> myBigList[(87, 342, 217, 998, 500)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: invalid index
>>> myBigList[[87, 342, 217, 998, 500]]
array([ 87, 342, 217, 998, 500])
>>> myBigList[numpy.array([87, 342, 217, 998, 500])]
array([ 87, 342, 217, 998, 500])

Il tuplenon funziona allo stesso modo di quelli sono fette.


2
Preferibilmente come composizione di una lista [myBigList[i] for i in [87, 342, 217, 998, 500]], ma questo approccio mi piace di più.
zeekay

@MedhatHelmy Questo è già nella risposta. La terza opzione utilizzata from operator import itemgetternella parte di inizializzazione di python -mtimeit.
Dan D.

Mi chiedo, solo dal punto di vista del design del linguaggio, perché myBigList[(87, 342, 217, 998, 500)]non funziona quando myBigListè un normale Python list? Quando provo, ottengo TypeError: list indices must be integers or slices, not tuple. Sarebbe molto più semplice che scrivere a macchina la comprensione: è coinvolto un problema di progettazione / implementazione del linguaggio?
sparc_spread

@sparc_spread, questo perché listsin Python accetta solo interi o slice. Il passaggio di un numero intero assicura che solo un elemento venga recuperato da un elenco esistente. Il passaggio di una slice assicura che una parte di essa venga recuperata, ma passare una tupla è come passare un data-type ( tuple) come argomento a un altro data-type ( list) che è sintatticamente errato.
amanb

48

Che dire di questo:

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')

2
Questo è il più sexy finora. Adoro quel operatormodulo!
jathanism

10

Non è integrato, ma puoi creare una sottoclasse di list che accetta le tuple come "indici" se lo desideri:

class MyList(list):

    def __getitem__(self, index):
        if isinstance(index, tuple):
            return [self[i] for i in index]
        return super(MyList, self).__getitem__(index)


seq = MyList("foo bar baaz quux mumble".split())
print seq[0]
print seq[2,4]
print seq[1::2]

stampa

foo
['baaz', 'mumble']
['bar', 'quux']

2
(+1) Ottima soluzione! Con questa estensione, la gestione degli array in Python inizia a sembrare molto R o Matlab.
Assad Ebrahim

7

Forse è necessaria una comprensione della lista:

L = ['a', 'b', 'c', 'd', 'e', 'f']
print [ L[index] for index in [1,3,5] ]

produce:

['b', 'd', 'f']

È quello che stai cercando?


6
>>> map(myList.__getitem__, (2,2,1,3))
('baz', 'baz', 'bar', 'quux')

Puoi anche creare la tua Listclasse che supporta le tuple come argomenti __getitem__se vuoi essere in grado di farlo myList[(2,2,1,3)].


Sebbene funzioni, di solito non è una buona idea invocare direttamente le variabili magiche. È meglio usare una comprensione dell'elenco o un modulo di supporto come operator.
jathanism

@ jathanism: devo rispettosamente dissentire. Tuttavia, se sei preoccupato per la compatibilità con le versioni successive (al contrario di pubblico / privato) posso sicuramente vedere da dove vieni.
ninjagecko

Ecco da dove vengo. :) A seguito di questo, è lo stesso motivo per cui è meglio usare len(myList)sopra myList.__len__().
jathanism

una soluzione creativa Non penso sia una cattiva idea invocare la variabile magica. il programmatore seleziona il modo preferito in base alle circostanze di programmazione.
Jacob CUI

2

Voglio solo sottolineare che anche la sintassi di itemgetter sembra davvero pulita, ma è un po 'lenta quando viene eseguita su un elenco di grandi dimensioni.

import timeit
from operator import itemgetter
start=timeit.default_timer()
for i in range(1000000):
    itemgetter(0,2,3)(myList)
print ("Itemgetter took ", (timeit.default_timer()-start))

Itemgetter ha preso 1.065209062149279

start=timeit.default_timer()
for i in range(1000000):
    myList[0],myList[2],myList[3]
print ("Multiple slice took ", (timeit.default_timer()-start))

Più slice hanno richiesto 0.6225321444745759


Primo snippet, per favore aggiungi myList = np.array(range(1000000))altrimenti otterrai un errore.
Cloud Cho

1

Un'altra possibile soluzione:

sek=[]
L=[1,2,3,4,5,6,7,8,9,0]
for i in [2, 4, 7, 0, 3]:
   a=[L[i]]
   sek=sek+a
print (sek)

0

come spesso quando hai un array numpy booleano come mask

[mylist[i] for i in np.arange(len(mask), dtype=int)[mask]]

Un lambda che funziona per qualsiasi sequenza o np.array:

subseq = lambda myseq, mask : [myseq[i] for i in np.arange(len(mask), dtype=int)[mask]]

newseq = subseq(myseq, mask)

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.