Явно выбирайте элементы из списка или кортежа

120

У меня есть следующий список Python (также может быть кортеж):

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

я могу сказать

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

Как явно выбрать элементы, индексы которых не имеют определенных шаблонов? Например, я хочу выбрать [0,2,3]. Или я хочу выбрать из очень большого списка из 1000 пунктов [87, 342, 217, 998, 500]. Есть ли какой-нибудь синтаксис Python для этого? Что-то вроде:

>>> myBigList[87, 342, 217, 998, 500]
Комплект
источник
1
Это похоже на дубликат. У другого вопроса больше голосов, но похоже, что у него лучший ответ с указанием времени.
AnnanFay

Ответы:

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

Я сравнил ответы с python 2.5.2:

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

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

  • 22,7 мкс: itemgetter(87, 342, 217, 998, 500)(myBigList)

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

Обратите внимание, что в Python 3 1-й был изменен на 4-й.


Другой вариант - начать с a, numpy.arrayкоторый позволяет индексировать через список или 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])

Это tupleне работает так же, как срезы.

Дэн Д.
источник
2
Желательно в виде списка [myBigList[i] for i in [87, 342, 217, 998, 500]], но мне больше всего нравится этот подход.
zeekay 09
@MedhatHelmy Это уже в ответе. Третий вариант, используемый from operator import itemgetterв части инициализации python -mtimeit.
Дэн Д.
Интересно, просто с точки зрения языкового дизайна, почему myBigList[(87, 342, 217, 998, 500)]не работает, когда myBigListэто обычный питон list? Когда я пытаюсь это сделать, я получаю TypeError: list indices must be integers or slices, not tuple. Это было бы намного проще, чем печатать понимание - есть ли проблема с языковым дизайном / реализацией?
sparc_spread
@sparc_spread, это потому, что listsв Python принимают только целые числа или фрагменты. Передача целого числа гарантирует, что из существующего списка будет извлечен только один элемент. Передача фрагмента гарантирует, что его часть будет извлечена, но передача кортежа похожа на передачу типа данных ( tuple) в качестве аргумента другому типу данных ( list), который синтаксически неверен.
amanb
48

Как насчет этого:

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')
Marcin
источник
2
На данный момент это самый сексуальный вариант. Обожаю этот operatorмодуль!
jathanism 09
10

Он не является встроенным, но вы можете создать подкласс списка, который принимает кортежи как «индексы», если хотите:

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]

печать

foo
['baaz', 'mumble']
['bar', 'quux']
Мэтт Андерсон
источник
2
(+1) Отличное решение! С этим расширением обработка массивов в Python начинает больше походить на R или Matlab.
Асад Ибрагим,
7

Может быть, понимание списка в порядке:

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

Производит:

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

Это то, что вы ищете?

Дэн Витковски
источник
6
>>> map(myList.__getitem__, (2,2,1,3))
('baz', 'baz', 'bar', 'quux')

Вы также можете создать свой собственный Listкласс, который поддерживает кортежи в качестве аргументов, __getitem__если хотите myList[(2,2,1,3)].

ninjagecko
источник
Хотя это работает, обычно не рекомендуется напрямую вызывать магические переменные. Вам лучше использовать понимание списка или вспомогательный модуль, например operator.
jathanism 09
@jathanism: Я должен с уважением не согласиться. Хотя, если вас беспокоит прямая совместимость (в отличие от общедоступной / частной), я определенно могу понять, откуда вы.
ninjagecko 09
Вот откуда я. :) По той же причине лучше использовать len(myList)over myList.__len__().
jathanism
творческое решение. Я не думаю, что вызывать волшебную переменную - плохая идея. программист выбирает предпочтительный способ в зависимости от обстоятельств программирования.
Джейкоб CUI
2

Я просто хочу отметить, что даже синтаксис itemgetter выглядит очень аккуратно, но при работе с большим списком он довольно медленный.

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 взял 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))

Множественный срез занял 0,6225321444745759

Вендао Лю
источник
Первый фрагмент, пожалуйста, добавьте, myList = np.array(range(1000000))иначе вы получите ошибку.
Cloud Cho
1

Другое возможное решение:

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
источник
0

как часто бывает, когда у вас есть логический массив numpy, например mask

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

Лямбда, которая работает для любой последовательности или np.array:

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

newseq = subseq(myseq, mask)

Тео Олстхорн
источник