Cómo obtener el enésimo elemento de una lista de Python o un valor predeterminado si no está disponible

144

Estoy buscando un equivalente en python dictionary.get(key, default)para listas. ¿Hay algún modismo para obtener el enésimo elemento de una lista o un valor predeterminado si no está disponible?

Por ejemplo, dada una lista myList que me gustaría obtener myList[0], o 5 si myListes una lista vacía.

Gracias.

usuario265454
fuente

Respuestas:

124
l[index] if index < len(l) else default

Para soportar índices negativos podemos usar:

l[index] if -len(l) <= index < len(l) else default
gruszczy
fuente
3
Sin embargo, no funciona si su lista no tiene un nombre, por ejemplo, en una comprensión de la lista.
Xiong Chiamiov
9
Parece que olvidaste el caso del índice negativo. P.ej. index == -1000000Debería volver default.
nodakai
3
@nodakai, buen punto. A veces me ha mordido esto. x[index] if 0 <= index < len(x) else defaultSería mejor si indexalguna vez puede ser negativo.
Ben Hoyt
3
@nodakai wow: ese es un gran ejemplo de por qué puede ser mejor usar try / except que intentar codificar correctamente la prueba para que nunca falle. Todavía no me gusta confiar en try / except, para un caso que sé que sucederá, pero esto aumenta mi disposición a considerarlo.
ToolmakerSteve
66
@ToolmakerSteve: su fórmula para valid_index()es incorrecta. Los índices negativos son legales en Python, deberían serlo -len(l) <= index < len(l).
Tim Pietzcker
57
try:
   a = b[n]
except IndexError:
   a = default

Editar: eliminé la verificación de TypeError; probablemente sea mejor dejar que la persona que llama maneje esto.

Tim Pietzcker
fuente
3
Me gusta esto. Simplemente envuélvalo alrededor de una función para que pueda llamarlo con un iterable como argumento. Más fácil de leer.
Noufal Ibrahim
35
(a[n:]+[default])[0]

Esto probablemente es mejor a medida que se ahace más grande

(a[n:n+1]+[default])[0]

Esto funciona porque si a[n:]es una lista vacía sin => len(a)

Aquí hay un ejemplo de cómo funciona esto range(5)

>>> range(5)[3:4]
[3]
>>> range(5)[4:5]
[4]
>>> range(5)[5:6]
[]
>>> range(5)[6:7]
[]

Y la expresión completa

>>> (range(5)[3:4]+[999])[0]
3
>>> (range(5)[4:5]+[999])[0]
4
>>> (range(5)[5:6]+[999])[0]
999
>>> (range(5)[6:7]+[999])[0]
999
John La Rooy
fuente
55
¿Escribirías esto en código real sin un comentario para explicarlo?
Peter Hansen
1
@ Peter Hansen, solo si estaba jugando al golf;) Sin embargo, funciona en todas las versiones de Python. La respuesta aceptada solo funciona en 2.5+
John La Rooy
3
Sin embargo, invita a crear 3 listas temporales y acceder a 2 índices para seleccionar un elemento.
Joachim Jablon
Si bien nunca haría esto en código "real", es una buena línea concisa y concisa que puedo usar fácilmente en una REPL de python, así que obtienes mi voto positivo.
Cookyt
next(iter(lst[i:i+1]), default)- Solo otra entrada en la críptica competencia de frases ingeniosas.
jfs
28

Acabo de descubrir que:

next(iter(myList), 5)

iter(l)devuelve un iterador myList, next()consume el primer elemento del iterador y genera un StopIterationerror, excepto si se llama con un valor predeterminado, que es el caso aquí, el segundo argumento,5

Esto solo funciona cuando desea el primer elemento, que es el caso en su ejemplo, pero no en el texto de su pregunta, así que ...

Además, no necesita crear listas temporales en la memoria y funciona para cualquier tipo de iterativo, incluso si no tiene un nombre (vea el comentario de Xiong Chiamiov sobre la respuesta de gruszczy)

Joachim Jablon
fuente
3
Combinado con las otras respuestas: next(iter(myList[n:n+1]), 5) ahora funciona para el nelemento th.
Alfe
Eso no funcionaría con no listas. En este punto, intente: excepto que un IndexError se lee como una mejor idea en mi humilde opinión. Además, crea una lista en la memoria.
Joachim Jablon
La tryvariante no es de una sola línea (pregunte pedido por OP). Solo funciona para listas porque myListes una lista según lo especificado por OP (para ser precisos, es algo indexable). Crear una copia en la memoria no es costoso aquí porque estoy creando una lista de un solo elemento (o ninguno) aquí. Claro, un poco por encima, pero no vale la pena mencionarlo a menos que lo haga millones de veces en un bucle. Por cierto, creo que crear y atrapar una IndexErrorexcepción es probablemente más costoso.
Alfe
la legibilidad cuenta :) Si estás en el punto en el que elevar un IndexError tiene un costo significativo, tal vez deberías estar haciendo todo en Python.
Joachim Jablon
20
(L[n:n+1] or [somedefault])[0]
Ignacio Vazquez-Abrams
fuente
1
+1 porque esto me hizo ir a aprender lo que [] or ...hizo. Sin embargo, personalmente solo usaría la solución aceptada, ya que se lee fácilmente (para principiantes). De acuerdo, envolverlo en una 'definición' con comentarios haría que eso no sea un problema.
ToolmakerSteve
¿Qué pasa si L[n] == Falseo L[n] == Noneo L[n] == []o más globalmente todo lo que se evalúa como falsa?
Joachim Jablon
@JoachimJablon: Todavía funciona. El segmento devuelve una lista y [False]es verdadero.
Ignacio Vazquez-Abrams
Oh, no me di cuenta de que la lista estaba marcada y no del valor, de hecho.
Joachim Jablon
¿Por qué envuelves esto en una tupla? Creo myval = l[n:n+1] or [somedefault]que funcionaría bien?
Rotareti
8

... buscando un equivalente en python dict.get(key, default)para listas

Hay una receta de itertools que hace esto para iterables generales. Para su comodidad, puede > pip install more_itertoolsimportar e importar esta biblioteca de terceros que implementa tales recetas para usted:

Código

import more_itertools as mit


mit.nth([1, 2, 3], 0)
# 1    

mit.nth([], 0, 5)
# 5    

Detalle

Aquí está la implementación de la nthreceta:

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(itertools.islice(iterable, n, None), default)

Al igual dict.get(), esta herramienta devuelve un valor predeterminado para los índices que faltan. Se aplica a iterables generales:

mit.nth((0, 1, 2), 1)                                      # tuple
# 1

mit.nth(range(3), 1)                                       # range generator (py3)
# 1

mit.nth(iter([0, 1, 2]), 1)                                # list iterator 
# 1  
pylang
fuente
2

Una solución barata es realmente hacer un dict con enumerar y usar .get()como de costumbre, como

 dict(enumerate(l)).get(7, my_default)
Escorchio
fuente
1

Combinando @ Joachim's con lo anterior, podría usar

next(iter(my_list[index:]), default)

Ejemplos:

next(iter(range(10)[8:]), 11)
8
>>> next(iter(range(10)[12:]), 11)
11

O, tal vez más claro, pero sin el len

my_list[index] if my_list[index:] else default
serv-inc
fuente
1

Usando Python 3.4's contextlib.suppress(exceptions)para construir un getitem()método similar a getattr().

import contextlib

def getitem(iterable, index, default=None):
    """Return iterable[index] or default if IndexError is raised."""
    with contextlib.suppress(IndexError):
        return iterable[index]
    return default
Harvey
fuente