Comment obtenir le nième élément d'une liste python ou une valeur par défaut s'il n'est pas disponible

144

Je recherche un équivalent en python de dictionary.get(key, default)for lists. Y a-t-il un idiome de ligne pour obtenir le nième élément d'une liste ou une valeur par défaut si elle n'est pas disponible?

Par exemple, étant donné une liste myList que je voudrais obtenir myList[0], ou 5 si myListest une liste vide.

Merci.

user265454
la source

Réponses:

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

Pour prendre en charge les indices négatifs, nous pouvons utiliser:

l[index] if -len(l) <= index < len(l) else default
gruszczy
la source
3
Cela ne fonctionne pas si votre liste n'a pas de nom, par exemple, dans une compréhension de liste.
Xiong Chiamiov
9
Vous semblez avoir oublié le cas de l'indice négatif. Par exemple. index == -1000000devrait revenir default.
nodakai
3
@nodakai, bon point. J'ai parfois été mordu par ça. x[index] if 0 <= index < len(x) else defaultserait mieux si indexpeut jamais être négatif.
Ben Hoyt
3
@nodakai wow - c'est un excellent exemple de la raison pour laquelle il peut être préférable d'utiliser try / except, plutôt que d'essayer de coder correctement le test pour qu'il n'échoue jamais. Je n'aime toujours pas me fier à essayer / sauf pour un cas dont je sais qu'il va se produire, mais cela augmente ma volonté de le considérer.
ToolmakerSteve
6
@ToolmakerSteve: Votre formule pour valid_index()est incorrecte. Les indices négatifs sont légaux en Python - cela devrait l'être -len(l) <= index < len(l).
Tim Pietzcker
57
try:
   a = b[n]
except IndexError:
   a = default

Edit: J'ai supprimé la vérification de TypeError - probablement mieux de laisser l'appelant gérer cela.

Tim Pietzcker
la source
3
J'aime ça. Enveloppez-le simplement autour d'une fonction pour pouvoir l'appeler avec un itérable comme argument. Plus facile à lire.
Noufal Ibrahim
35
(a[n:]+[default])[0]

C'est probablement mieux en agrossissant

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

Cela fonctionne car si a[n:]est une liste vide sin => len(a)

Voici un exemple de comment cela fonctionne avec range(5)

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

Et la pleine expression

>>> (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
la source
5
Voulez-vous écrire cela dans le code réel sans un commentaire pour l'expliquer?
Peter Hansen
1
@Peter Hansen, seulement si je jouais au golf;) Cependant, cela fonctionne sur toutes les versions de Python. La réponse acceptée ne fonctionne que sur 2.5+
John La Rooy
3
Cela implique la création de 3 listes temporaires et l'accès à 2 index afin de sélectionner un élément, cependant.
Joachim Jablon
Bien que je ne fasse jamais cela dans du "vrai" code, c'est un joli one-liner concis que je peux facilement utiliser dans une REPL python, donc vous obtenez mon vote positif.
Cookyt
next(iter(lst[i:i+1]), default)- juste une autre entrée dans la compétition cryptique laid one-liners.
jfs
28

Je viens de découvrir que:

next(iter(myList), 5)

iter(l)renvoie un itérateur sur myList, next()consomme le premier élément de l'itérateur, et lève une StopIterationerreur sauf s'il est appelé avec une valeur par défaut, ce qui est le cas ici, le deuxième argument,5

Cela ne fonctionne que lorsque vous voulez le 1er élément, ce qui est le cas dans votre exemple, mais pas dans le texte de votre question, donc ...

De plus, il n'a pas besoin de créer de listes temporaires en mémoire et fonctionne pour tout type d'itérable, même s'il n'a pas de nom (voir le commentaire de Xiong Chiamiov sur la réponse de gruszczy)

Joachim Jablon
la source
3
Combiné avec les autres réponses: next(iter(myList[n:n+1]), 5) maintenant cela fonctionne pour le ne élément.
Alfe
Cela ne fonctionnerait pas avec les non-listes. À ce stade, essayez: sauf-ing un IndexError se lit comme une meilleure idée IMHO. En outre, il crée une liste en mémoire.
Joachim Jablon
La tryvariante n'est pas une ligne unique (demandée par OP). Cela ne fonctionne que pour les listes car il myLists'agit d'une liste telle que spécifiée par OP (pour être précis, c'est quelque chose d'indexable). Créer une copie en mémoire n'est pas coûteux ici car je crée ici une liste d'un seul élément (ou aucun). Bien sûr, un peu de surcharge, mais cela ne vaut pas la peine d'être mentionné à moins que vous ne le fassiez des millions de fois en boucle. Btw, je suppose que créer et attraper une IndexErrorexception est probablement plus coûteux.
Alfe
la lisibilité compte :) Si vous êtes au point où augmenter une IndexError a un coût important, vous devriez peut-être tout faire en Python.
Joachim Jablon
20
(L[n:n+1] or [somedefault])[0]
Ignacio Vazquez-Abrams
la source
1
+1 parce que cela m'a fait apprendre ce que j'ai [] or ...fait. Cependant, personnellement, j'utiliserais simplement la solution acceptée, car elle se lit facilement (pour les novices). Certes, l'envelopper dans un «def» avec un commentaire en ferait largement un non-problème.
ToolmakerSteve
Que faire si L[n] == Falseou L[n] == Noneou L[n] == []ou plus globalement tout ce qui Equivaut à Faux?
Joachim Jablon
@JoachimJablon: Fonctionne toujours. La tranche renvoie une liste et [False]est vraie.
Ignacio Vazquez-Abrams
Oh, je n'ai pas réalisé que la liste avait été vérifiée et non la valeur, en effet.
Joachim Jablon
Pourquoi enveloppez-vous cela dans un tuple? Je pense que myval = l[n:n+1] or [somedefault]cela fonctionnerait très bien?
Rotareti
8

... à la recherche d'un équivalent en python de dict.get(key, default)for lists

Il existe des recettes itertools qui le font pour les itérables généraux. Pour plus de commodité, vous pouvez > pip install more_itertoolset importer cette bibliothèque tierce qui implémente de telles recettes pour vous:

Code

import more_itertools as mit


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

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

Détail

Voici la mise en œuvre de la nthrecette:

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

Comme dict.get(), cet outil renvoie une valeur par défaut pour les index manquants. Elle s'applique aux itérables généraux:

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
la source
2

Une solution bon marché consiste à créer un dict avec enumerate et à utiliser .get()comme d'habitude, comme

 dict(enumerate(l)).get(7, my_default)
Scorchio
la source
1

En combinant @ Joachim avec ce qui précède, vous pouvez utiliser

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

Exemples:

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

Ou, peut-être plus clair, mais sans le len

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

Utiliser Python 3.4 contextlib.suppress(exceptions)pour construire une getitem()méthode similaire à 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
la source