Pilih item secara eksplisit dari daftar atau tuple

120

Saya memiliki daftar Python berikut (juga bisa menjadi tupel):

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

Bisa saya katakan

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

Bagaimana saya secara eksplisit memilih item yang indeksnya tidak memiliki pola tertentu? Misalnya, saya ingin memilih [0,2,3]. Atau dari daftar 1000 item yang sangat besar, saya ingin memilih [87, 342, 217, 998, 500]. Apakah ada sintaks Python yang melakukan itu? Sesuatu yang terlihat seperti:

>>> myBigList[87, 342, 217, 998, 500]
Kit
sumber
1
Ini tampaknya duplikat. Pertanyaan lain memiliki lebih banyak suara tetapi ini sepertinya memiliki jawaban yang lebih baik dengan pengaturan waktu.
AnnanFay

Jawaban:

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

Saya membandingkan jawaban dengan 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] )

Perhatikan bahwa di Python 3, yang pertama diubah menjadi sama dengan yang ke-4.


Pilihan lain adalah memulai dengan numpy.arrayyang memungkinkan pengindeksan melalui daftar atau 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])

Cara tuplekerjanya tidak sama seperti irisan.

Dan D.
sumber
2
Lebih disukai sebagai komp daftar [myBigList[i] for i in [87, 342, 217, 998, 500]], tapi saya suka pendekatan ini yang terbaik.
zeekay
@MedhatHelmy Itu sudah ada di jawabannya. Opsi ketiga yang digunakan from operator import itemgetterdi bagian inisialisasi python -mtimeit.
Dan D.
Saya bertanya-tanya, hanya dari perspektif desain bahasa, mengapa myBigList[(87, 342, 217, 998, 500)]tidak berfungsi ketika myBigListpython biasa list? Ketika saya mencoba itu saya dapatkan TypeError: list indices must be integers or slices, not tuple. Itu akan jauh lebih mudah daripada mengetikkan pemahaman - apakah ada masalah desain / implementasi bahasa yang terlibat?
sparc_spread
@sparc_spread, ini karena listsdi Python hanya menerima integer atau irisan. Meneruskan integer memastikan bahwa hanya satu item yang diambil dari daftar yang ada. Meneruskan slice memastikan sebagian darinya diambil, tetapi meneruskan tupel seperti meneruskan tipe data ( tuple) sebagai argumen ke tipe data lain ( list) yang secara sintaksis salah.
amanb
48

Bagaimana dengan ini:

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')
Marcin
sumber
2
Ini yang paling seksi sejauh ini. Suka operatormodul itu!
jatanisme
10

Ini bukan bawaan, tetapi Anda dapat membuat subkelas dari daftar yang menggunakan tupel sebagai "indeks" jika Anda ingin:

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]

pencetakan

foo
['baaz', 'mumble']
['bar', 'quux']
Matt Anderson
sumber
2
(+1) Solusi yang rapi! Dengan ekstensi ini, menangani array dengan Python mulai terlihat seperti R atau Matlab.
Assad Ebrahim
7

Mungkin pemahaman daftar sesuai:

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

Menghasilkan:

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

Itukah yang kamu cari?

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

Anda juga dapat membuat Listkelas Anda sendiri yang mendukung tupel sebagai argumen __getitem__jika Anda ingin melakukannya myList[(2,2,1,3)].

ninjagecko
sumber
Meskipun ini berhasil, biasanya bukan ide yang baik untuk memanggil variabel ajaib secara langsung. Anda lebih baik menggunakan pemahaman daftar atau modul pembantu seperti operator.
jatanisme
@ Jatanisme: Saya harus dengan hormat tidak setuju. Meskipun jika Anda khawatir tentang kompatibilitas ke depan (sebagai lawan dari publik / pribadi), saya pasti dapat melihat dari mana Anda berasal.
ninjagecko
Dari sanalah saya berasal. :) Setelah itu, itu alasan yang sama mengapa lebih baik menggunakan len(myList)lebih myList.__len__().
Jatanisme
solusi kreatif. Saya tidak berpikir itu ide yang buruk untuk menggunakan variabel ajaib. programmer memilih cara yang mereka sukai berdasarkan keadaan pemrograman.
Jacob CUI
2

Saya hanya ingin menunjukkan, bahkan sintaks itemgetter terlihat sangat rapi, tetapi agak lambat ketika tampil di daftar besar.

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 mengambil 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))

Irisan ganda mengambil 0,6225321444745759

Wendao Liu
sumber
Potongan pertama, tambahkan myList = np.array(range(1000000))jika tidak Anda akan mendapatkan kesalahan.
Cloud Cho
1

Solusi lain yang mungkin:

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
sumber
0

seperti sering kali jika Anda memiliki array numpy boolean seperti mask

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

Lambda yang berfungsi untuk semua urutan atau np.array:

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

newseq = subseq(myseq, mask)

theo olsthoorn
sumber