Selecione explicitamente os itens de uma lista ou tupla

120

Eu tenho a seguinte lista Python (também pode ser uma tupla):

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

eu posso dizer

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

Como escolho explicitamente itens cujos índices não têm padrões específicos? Por exemplo, eu quero selecionar [0,2,3]. Ou, em uma lista muito grande de 1000 itens, desejo selecionar [87, 342, 217, 998, 500]. Existe alguma sintaxe Python que faz isso? Algo parecido com:

>>> myBigList[87, 342, 217, 998, 500]
Kit
fonte
1
Este parece ser um duplicado. A outra pergunta tem mais votos positivos, mas parece que tem uma resposta melhor com intervalos.
AnnanFay

Respostas:

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

Eu comparei as respostas com 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] )

Observe que no Python 3, o primeiro foi alterado para ser o mesmo que o quarto.


Outra opção seria começar com um numpy.arrayque permite a indexação por meio de uma lista ou 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])

O tuplenão funciona da mesma maneira que aquelas fatias.

Dan D.
fonte
2
De preferência como um comp de lista [myBigList[i] for i in [87, 342, 217, 998, 500]], mas eu gosto mais dessa abordagem.
zeekay
@MedhatHelmy Isso já está na resposta. A terceira opção usada from operator import itemgetterna parte de inicialização do python -mtimeit.
Dan D.
Eu me pergunto, apenas de uma perspectiva de design de linguagem, por myBigList[(87, 342, 217, 998, 500)]que não funciona quando myBigListé um python regular list? Quando eu tento, eu entendo TypeError: list indices must be integers or slices, not tuple. Isso seria muito mais fácil do que digitar a compreensão - há um problema de design / implementação de linguagem envolvido?
sparc_spread
@sparc_spread, isso ocorre porque listsem Python só aceita inteiros ou fatias. Passar um número inteiro garante que apenas um item seja recuperado de uma lista existente. Passar uma fatia garante que uma parte dela seja recuperada, mas passar uma tupla é como passar um tipo de dados ( tuple) como um argumento para outro tipo de dados ( list) que é sintaticamente incorreto.
amanb
48

Que tal isso:

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')
Marcin
fonte
2
Este é o mais sexy até agora. Amei esse operatormódulo!
jathanismo
10

Não é integrado, mas você pode fazer uma subclasse de lista que considera as tuplas como "índices" se desejar:

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]

impressão

foo
['baaz', 'mumble']
['bar', 'quux']
Matt Anderson
fonte
2
(+1) Solução bacana! Com essa extensão, o tratamento de arrays em Python começa a parecer muito com R ou Matlab.
Assad Ebrahim
7

Talvez seja necessária uma compreensão de lista:

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

Produz:

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

É isso que você está procurando?

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

Você também pode criar sua própria Listclasse que suporta tuplas como argumentos para __getitem__se você quiser myList[(2,2,1,3)].

ninjagecko
fonte
Embora isso funcione, geralmente não é uma boa ideia invocar variáveis ​​mágicas diretamente. É melhor usar uma compreensão de lista ou um módulo auxiliar como operator.
jathanismo
@jathanism: tenho que discordar respeitosamente. Porém, se você estiver preocupado com a compatibilidade com versões futuras (em oposição a público / privado), posso definitivamente ver de onde você está vindo.
ninjagecko
É daí que venho. :) Depois disso, é o mesmo motivo pelo qual é melhor usar len(myList)over myList.__len__().
jathanism
uma solução criativa. Não acho má ideia invocar a variável mágica. o programador seleciona sua forma preferida com base nas circunstâncias de programação.
Jacob CUI
2

Eu só quero apontar, até mesmo a sintaxe do itemgetter parece muito legal, mas é meio lenta quando executada em uma lista grande.

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

Múltiplas fatias levaram 0,6225321444745759

Wendao Liu
fonte
Primeiro trecho, adicione, myList = np.array(range(1000000))caso contrário, você obterá um erro.
Cloud Cho
1

Outra solução possível:

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

como frequentemente, quando você tem uma matriz numpy booleana como mask

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

Um lambda que funciona para qualquer sequência ou np.array:

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

newseq = subseq(myseq, mask)

theo olsthoorn
fonte