Come ottenere l'ennesimo elemento di un elenco Python o un valore predefinito se non disponibile


144

Sto cercando un equivalente in Python dictionary.get(key, default)per le liste. Esiste un idioma di linea per ottenere l'ennesimo elemento di un elenco o un valore predefinito se non disponibile?

Ad esempio, dato un elenco myList che vorrei ottenere myList[0], o 5 se myListè un elenco vuoto.

Grazie.

Risposte:


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

Per supportare gli indici negativi possiamo usare:

l[index] if -len(l) <= index < len(l) else default

3
Tuttavia, non funziona se l'elenco non ha un nome, ad esempio in una comprensione dell'elenco.
Xiong Chiamiov

9
Sembra che tu abbia dimenticato il caso dell'indice negativo. Per esempio. index == -1000000dovrebbe tornare default.
nodakai,

3
@nodakai, buon punto. A volte sono stato morso da questo. x[index] if 0 <= index < len(x) else defaultsarebbe meglio se indexmai potesse essere negativo.
Ben Hoyt,

3
@nodakai wow - questo è un ottimo esempio del perché potrebbe essere meglio usare try / tranne, piuttosto che provare a codificare correttamente il test in modo che non fallisca mai. Non mi piace ancora fare affidamento su try / tranne, per un caso che so accadrà, ma questo aumenta la mia volontà di considerarlo.
ToolmakerSteve

6
@ToolmakerSteve: la formula per valid_index()non è corretta. Gli indici negativi sono legali in Python - dovrebbe essere -len(l) <= index < len(l).
Tim Pietzcker,

57
try:
   a = b[n]
except IndexError:
   a = default

Modifica: ho rimosso il controllo per TypeError - probabilmente è meglio lasciare che sia il chiamante a gestirlo.


3
Mi piace questo. Basta avvolgerlo attorno a una funzione in modo da poterlo chiamare con un iterabile come argomento. Più facile da leggere.
Noufal Ibrahim,

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

Questo probabilmente è meglio perché adiventa più grande

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

Questo funziona perché if a[n:]è un elenco vuoto ifn => len(a)

Ecco un esempio di come funziona range(5)

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

E la piena espressione

>>> (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

5
Scriveresti questo nel codice reale senza un commento per spiegarlo?
Peter Hansen,

1
@Peter Hansen, solo se stavo giocando a golf;) Tuttavia funziona su tutte le versioni di Python. La risposta accettata funziona solo su 2.5+
John La Rooy,

3
Tuttavia, crea 3 elenchi temporanei e accede a 2 indici per selezionare un elemento.
Joachim Jablon,

Anche se non lo farei mai nel codice "reale", è una riga concisa piacevole che posso facilmente usare in un REPL Python, quindi ottieni il mio voto.
Cookyt,

next(iter(lst[i:i+1]), default)- solo un'altra voce nella brutta competizione enigmatica con una sola linea.
jfs

28

Ho appena scoperto che:

next(iter(myList), 5)

iter(l)restituisce un iteratore attivo myList, next()consuma il primo elemento dell'iteratore e genera un StopIterationerrore, tranne se chiamato con un valore predefinito, che è il caso qui, il secondo argomento,5

Funziona solo quando vuoi il primo elemento, come nel tuo esempio, ma non nel testo della tua domanda, quindi ...

Inoltre, non è necessario creare elenchi temporanei in memoria e funziona per qualsiasi tipo di iterabile, anche se non ha un nome (vedere il commento di Xiong Chiamiov sulla risposta di gruszczy)


3
Combinato con le altre risposte: next(iter(myList[n:n+1]), 5) ora funziona per l' nelemento th.
Alfe,

Ciò non funzionerebbe con i non list. A questo punto, prova: tranne che un IndexError si legge come una migliore idea di IMHO. Inoltre, crea un elenco in memoria.
Joachim Jablon,

La tryvariante non è a una riga (chiedere richiesto dall'OP). Funziona solo per gli elenchi perché myListè un elenco come specificato da OP (per essere precisi, è qualcosa di indicizzabile). Creare una copia in memoria non è costoso qui perché sto creando un elenco di un singolo elemento (o nessuno) qui. Certo, un po 'sopraelevato, ma non vale la pena menzionarlo se non lo fai milioni di volte in un ciclo. A proposito, immagino che creare e catturare IndexErrorun'eccezione sia probabilmente più costoso.
Alfe,

la leggibilità conta :) Se sei nel punto in cui aumentare un IndexError ha un costo significativo, forse dovresti fare tutto in Python.
Joachim Jablon,

20
(L[n:n+1] or [somedefault])[0]

1
+1 perché questo mi ha fatto andare a sapere cosa ha [] or ...fatto. Tuttavia, utilizzerei personalmente solo la soluzione accettata, in quanto legge facilmente (per i principianti). Certo, avvolgendolo in una "def" con un commento, questo sarebbe in gran parte un non-problema.
ToolmakerSteve

Che cosa succede se L[n] == Falseo L[n] == Noneo L[n] == []più a livello globale qualcosa che viene valutato come False?
Joachim Jablon,

@JoachimJablon: funziona ancora. La sezione restituisce un elenco ed [False]è vera.
Ignacio Vazquez-Abrams,

Oh, non avevo capito che l'elenco era stato controllato e non il valore, anzi.
Joachim Jablon

Perché lo avvolgi in una tupla? Penso myval = l[n:n+1] or [somedefault]che funzionerebbe bene?
Rotareti

8

... alla ricerca di un equivalente in Python dict.get(key, default)per le liste

Esistono ricette itertools che lo fanno per iterabili generali. Per comodità, puoi > pip install more_itertoolse importare questa libreria di terze parti che implementa tali ricette per te:

Codice

import more_itertools as mit


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

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

Dettaglio

Ecco l'implementazione della nthricetta:

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

Ad esempio dict.get(), questo strumento restituisce un valore predefinito per gli indici mancanti. Si applica agli iterabili generali:

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  

2

Una soluzione economica è davvero fare un dict con enumerare e utilizzare .get()come al solito, come

 dict(enumerate(l)).get(7, my_default)

1

Combinando @ Joachim con quanto sopra, potresti usare

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

Esempi:

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

O, forse più chiaro, ma senza il len

my_list[index] if my_list[index:] else default

1

Usare Python 3.4 contextlib.suppress(exceptions)per costruire un getitem()metodo simile 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.