Явно виберіть елементи зі списку або кортеж


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

Відповіді:


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

Я порівняв відповіді з 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] )

Зауважте, що в Python 3 змінено 1-й на той самий, що й 4-й.


Іншим варіантом було б почати з того, 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

@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), який є синтаксично неправильним.
аманб

48

Як що до цього:

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

2
Це найсексуальніша поки що. Любіть цей operatorмодуль!
ятанізм

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)].


Хоча це працює, зазвичай не гарна ідея безпосередньо викликати магічні змінні. Вам краще скористатися розумінням списку або допоміжним модулем на зразок operator.
ятанізм

@jathanism: я з повагою не погоджуюся. Хоча якщо вас турбує сумісність вперед (на відміну від публічної / приватної), я точно можу побачити, звідки ви родом.
ninjagecko

От звідки я родом. :) Слідом за цим, це та сама причина, чому краще використовувати len(myList)більше myList.__len__().
ятанізм

креативне рішення. Я не думаю, що це погана ідея викликати магічну змінну. програміст вибирає бажаний спосіб, виходячи з обставин програмування.
Jacob 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))інакше ви отримаєте помилку.
Хмара Чо

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)

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)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.