Wyraźnie wybierz elementy z listy lub krotki

120

Mam następującą listę w Pythonie (może to być również krotka):

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

mogę powiedzieć

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

Jak jawnie wybrać towary, których indeksy nie mają określonych wzorców? Na przykład chcę wybrać [0,2,3]. Lub z bardzo dużej listy 1000 pozycji, które chcę wybrać [87, 342, 217, 998, 500]. Czy jest jakaś składnia Pythona, która to robi? Coś, co wygląda jak:

>>> myBigList[87, 342, 217, 998, 500]
Zestaw
źródło
1
To wydaje się być duplikatem. Drugie pytanie ma więcej głosów, ale wydaje się, że ma lepszą odpowiedź z czasem.
AnnanFay

Odpowiedzi:

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

Porównałem odpowiedzi z Pythonem 2.5.2:

  • 19,7 usek: [ myBigList[i] for i in [87, 342, 217, 998, 500] ]

  • 20,6 usek: map(myBigList.__getitem__, (87, 342, 217, 998, 500))

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

  • 24,6 usek: list( myBigList[i] for i in [87, 342, 217, 998, 500] )

Zwróć uwagę, że w Pythonie 3 pierwszy został zmieniony na taki sam jak czwarty.


Inną opcją byłoby rozpoczęcie od a, numpy.arrayktóry umożliwia indeksowanie za pomocą listy lub 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])

tupleNie działa w ten sam sposób jak te są plastry.

Dan D.
źródło
2
Najlepiej jako zestawienie list [myBigList[i] for i in [87, 342, 217, 998, 500]], ale to podejście podoba mi się najbardziej.
zeekay,
@MedhatHelmy To już jest w odpowiedzi. Trzecia opcja używana from operator import itemgetterw części inicjalizacyjnej python -mtimeit.
Dan D.
Zastanawiam się, z perspektywy projektowania języka, dlaczego myBigList[(87, 342, 217, 998, 500)]nie działa, gdy myBigListjest zwykłym Pythonem list? Kiedy próbuję, dostaję TypeError: list indices must be integers or slices, not tuple. Byłoby to o wiele łatwiejsze niż wpisanie zrozumienia - czy jest jakiś problem z projektem / implementacją języka?
sparc_spread
@sparc_spread, dzieje się tak, ponieważ listsw Pythonie akceptujemy tylko liczby całkowite lub wycinki. Przekazanie liczby całkowitej zapewnia, że ​​z istniejącej listy zostanie pobrany tylko jeden element. Przekazanie wycinka zapewnia pobranie jego części, ale przekazanie krotki jest podobne do przekazania danych typu ( tuple) jako argumentu do innego typu danych ( list), co jest niepoprawne składniowo.
amanb
48

A co z tym:

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')
Marcin
źródło
2
To jest jak dotąd najseksowniejsza. Uwielbiam ten operatormoduł!
jathanizm,
10

Nie jest wbudowana, ale jeśli chcesz, możesz utworzyć podklasę listy, która pobiera krotki jako „indeksy”:

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]

druk

foo
['baaz', 'mumble']
['bar', 'quux']
Matt Anderson
źródło
2
(+1) Zgrabne rozwiązanie! Dzięki temu rozszerzeniu obsługa tablic w Pythonie zaczyna wyglądać jak R lub Matlab.
Assad Ebrahim
7

Może warto posłuchać listy:

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

Produkuje:

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

Czy tego szukasz?

Dan Witkowski
źródło
6
>>> map(myList.__getitem__, (2,2,1,3))
('baz', 'baz', 'bar', 'quux')

Możesz także stworzyć własną Listklasę, która obsługuje krotki jako argumenty, __getitem__jeśli chcesz mieć taką możliwość myList[(2,2,1,3)].

ninjagecko
źródło
Chociaż to działa, zwykle nie jest dobrym pomysłem bezpośrednie wywoływanie magicznych zmiennych. Lepiej jest użyć funkcji rozumienia list lub modułu pomocniczego, takiego jak operator.
jatanizm,
@jathanism: Z całym szacunkiem się nie zgadzam. Chociaż jeśli obawiasz się kompatybilności z przodu (w przeciwieństwie do publicznego / prywatnego), zdecydowanie mogę zobaczyć, skąd pochodzisz.
ninjagecko,
Stąd pochodzę. :) Po tym, jest to ten sam powód, dla którego lepiej jest użyć len(myList)over myList.__len__().
jathanizm,
kreatywne rozwiązanie. nie sądzę, aby wywoływanie magicznej zmiennej to zły pomysł. programista wybiera preferowany sposób na podstawie warunków programowania.
Jacob CUI
2

Chcę tylko podkreślić, że nawet składnia itemgetter wygląda naprawdę schludnie, ale działa trochę wolno, gdy wykonuje się na dużej liście.

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 zajął 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))

Wiele wycinków zajęło 0,6225321444745759

Wendao Liu
źródło
Pierwszy fragment, dodaj, w myList = np.array(range(1000000))przeciwnym razie pojawi się błąd.
Cloud Cho
1

Inne możliwe rozwiązanie:

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)
fdante
źródło
0

jak często, gdy masz tablicę logiczną numpy, taką jak mask

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

Lambda, która działa dla dowolnej sekwencji lub np. Tablicy:

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

newseq = subseq(myseq, mask)

theo olsthoorn
źródło