Seleccionar elementos explícitamente de una lista o tupla

120

Tengo la siguiente lista de Python (también puede ser una tupla):

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

puedo decir

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

¿Cómo selecciono explícitamente elementos cuyos índices no tienen patrones específicos? Por ejemplo, quiero seleccionar [0,2,3]. O de una lista muy grande de 1000 elementos, quiero seleccionar [87, 342, 217, 998, 500]. ¿Existe alguna sintaxis de Python que haga eso? Algo que se parezca a:

>>> myBigList[87, 342, 217, 998, 500]
Equipo
fuente
1
Esto parece ser un duplicado. La otra pregunta tiene más votos positivos, pero parece que tiene una mejor respuesta con los tiempos.
AnnanFay

Respuestas:

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

Comparé las respuestas con 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] )

Tenga en cuenta que en Python 3, el primero se cambió para que sea el mismo que el cuarto.


Otra opción sería comenzar con una numpy.arrayque permita indexar a través de una lista o una 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])

El tupleno funciona de la misma manera que los cortes.

Dan D.
fuente
2
Preferiblemente como una composición de lista [myBigList[i] for i in [87, 342, 217, 998, 500]], pero me gusta más este enfoque.
zeekay
@MedhatHelmy Eso ya está en la respuesta. La tercera opción utilizada from operator import itemgetteren la parte de inicialización de python -mtimeit.
Dan D.
Me pregunto, solo desde la perspectiva del diseño del lenguaje, ¿por qué myBigList[(87, 342, 217, 998, 500)]no funciona cuando myBigListes una pitón normal list? Cuando lo intento, lo consigo TypeError: list indices must be integers or slices, not tuple. Eso sería mucho más fácil que escribir la comprensión: ¿hay algún problema de diseño / implementación del lenguaje involucrado?
sparc_spread
@sparc_spread, esto se debe a que listsen Python solo acepta enteros o porciones. Pasar un número entero asegura que solo se recupere un elemento de una lista existente. Pasar un segmento asegura que se recupere una parte, pero pasar una tupla es como pasar un tipo de datos ( tuple) como argumento a otro tipo de datos ( list) que es sintácticamente incorrecto.
amanb
48

¿Qué pasa con esto?

from operator import itemgetter
itemgetter(0,2,3)(myList)
('foo', 'baz', 'quux')
Marcin
fuente
2
Este es el más sexy hasta ahora. ¡Amo ese operatormódulo!
jathanismo
10

No está integrado, pero puede crear una subclase de lista que tome tuplas como "índices" si lo desea:

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]

impresión

foo
['baaz', 'mumble']
['bar', 'quux']
Matt Anderson
fuente
2
(+1) ¡Buena solución! Con esta extensión, el manejo de matrices en Python comienza a parecer mucho R o Matlab.
Assad Ebrahim
7

Quizás una lista de comprensión esté en orden:

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

Produce:

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

¿Es esto lo que estás buscando?

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

También puede crear su propia Listclase que admita tuplas como argumentos __getitem__si desea poder hacerlo myList[(2,2,1,3)].

ninjagecko
fuente
Si bien esto funciona, generalmente no es una buena idea invocar directamente variables mágicas. Es mejor usar una lista de comprensión o un módulo auxiliar como operator.
jathanismo
@jathanism: Tengo que estar respetuosamente en desacuerdo. Aunque si le preocupa la compatibilidad con versiones posteriores (en lugar de público / privado), definitivamente puedo ver de dónde viene.
ninjagecko
De ahí es de donde vengo. :) Después de eso, es la misma razón por la que es mejor usar len(myList)over myList.__len__().
jathanismo
una solución creativa No creo que sea una mala idea invocar la variable mágica. El programador selecciona su forma preferida en función de las circunstancias de programación.
Jacob CUI
2

Solo quiero señalar que incluso la sintaxis de itemgetter se ve muy bien, pero es un poco lento cuando se realiza en una 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 tomó 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))

Varios cortes tomaron 0.6225321444745759

Wendao Liu
fuente
Primer fragmento, agregue; de ​​lo myList = np.array(range(1000000))contrario, obtendrá un error.
Cloud Cho
1

Otra posible solución:

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

como a menudo cuando tienes una matriz numpy booleana como mask

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

Una lambda que funciona para cualquier secuencia o np.array:

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

newseq = subseq(myseq, mask)

theo olsthoorn
fuente