Jak uzyskać n-ty element listy Pythona lub domyślny, jeśli nie jest dostępny

144

Szukam odpowiednika dictionary.get(key, default)list w pythonie . Czy istnieje jeden idiom liniowy, aby uzyskać n-ty element listy lub wartość domyślną, jeśli nie jest dostępna?

Na przykład, biorąc pod uwagę listę mojaLista, którą chciałbym uzyskać myList[0], lub 5, jeśli myListjest to pusta lista.

Dzięki.

user265454
źródło

Odpowiedzi:

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

Do obsługi ujemnych indeksów możemy użyć:

l[index] if -len(l) <= index < len(l) else default
gruszczy
źródło
3
Nie działa, jeśli lista nie ma nazwy - na przykład w przypadku rozumienia listy.
Xiong Chiamiov
9
Wydaje się, że zapomniałeś o przypadku wskaźnika ujemnego. Na przykład. index == -1000000powinien wrócić default.
nodakai
3
@nodakai, dobra uwaga. Czasami mnie to ugryzło. x[index] if 0 <= index < len(x) else defaultbyłoby lepiej, gdyby indexkiedykolwiek mogło być negatywne.
Ben Hoyt
3
@nodakai wow - to świetny przykład tego, dlaczego lepiej jest użyć try / except, niż próbować poprawnie zakodować test, aby nigdy nie zawiódł. Nadal nie lubię polegać na próbach / z wyjątkiem przypadku, o którym wiem, że się wydarzy, ale to zwiększa moją gotowość do rozważenia tego.
ToolmakerSteve
6
@ToolmakerSteve: Twój wzór na valid_index()jest nieprawidłowy. Indeksy ujemne legalne w Pythonie - powinno być -len(l) <= index < len(l).
Tim Pietzcker
57
try:
   a = b[n]
except IndexError:
   a = default

Edycja: usunąłem sprawdzanie TypeError - prawdopodobnie lepiej pozwolić wywołującemu to obsłużyć.

Tim Pietzcker
źródło
3
Lubię to. Po prostu zawiń go wokół funkcji, aby można było ją wywołać z iterowalnym argumentem. Łatwiejsze do odczytania.
Noufal Ibrahim
35
(a[n:]+[default])[0]

Jest to prawdopodobnie lepsze, gdy astaje się większe

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

To działa, ponieważ if a[n:]jest pustą listą ifn => len(a)

Oto przykład tego, jak to działa range(5)

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

I pełen wyraz

>>> (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
źródło
5
Czy napisałbyś to w rzeczywistym kodzie bez komentarza wyjaśniającego?
Peter Hansen,
1
@Peter Hansen, tylko gdybym grał w golfa;) Jednak działa na wszystkich wersjach Pythona. Zaakceptowana odpowiedź działa tylko na 2.5+
John La Rooy,
3
Uwielbia jednak tworzenie 3 tymczasowych list i uzyskiwanie dostępu do 2 indeksów w celu wybrania pozycji.
Joachim Jablon
Chociaż nigdy nie zrobiłbym tego w „prawdziwym” kodzie, jest to ładny, zwięzły, jednolinijkowy tekst, którego mogę z łatwością użyć w Pythonie REPL, więc otrzymujesz moje poparcie.
Cookyt
next(iter(lst[i:i+1]), default)- to kolejny wpis w tajemniczej, brzydkiej konkurencji jednokierunkowej.
jfs
28

Właśnie odkryłem, że:

next(iter(myList), 5)

iter(l)zwraca iterator włączony myList, next()zużywa pierwszy element iteratora i zgłasza StopIterationbłąd, chyba że wywołanie z wartością domyślną, co ma miejsce w tym przypadku, drugi argument,5

Działa to tylko wtedy, gdy potrzebujesz pierwszego elementu, co ma miejsce w twoim przykładzie, ale nie w tekście pytania, więc ...

Dodatkowo nie musi tworzyć tymczasowych list w pamięci i działa z każdym rodzajem iterowalnym, nawet jeśli nie ma nazwy (patrz komentarz Xionga Chiamiova do odpowiedzi gruszczy)

Joachim Jablon
źródło
3
W połączeniu z innymi odpowiedziami: next(iter(myList[n:n+1]), 5) Teraz działa dla ntego elementu.
Alfe
To nie zadziała w przypadku list innych niż listy. W tym momencie spróbuj: z wyjątkiem tego, że IndexError czyta jak lepszy pomysł IMHO. Tworzy również listę w pamięci.
Joachim Jablon
tryWariant nie jest jedno-liner (zapytaj zadawane przez OP). Działa tylko w przypadku list, ponieważ myListjest to lista określona przez OP (dokładniej, jest to coś indeksowalnego). Tworzenie kopii w pamięci nie jest tutaj kosztowne, ponieważ tworzę tutaj listę jednego (lub żadnego) elementu. Jasne, trochę narzutów, ale nie warto o nich wspominać, chyba że robisz to miliony razy w pętli. Przy okazji, myślę, że tworzenie i przechwytywanie IndexErrorwyjątku jest prawdopodobnie bardziej kosztowne.
Alfe
liczy się czytelność :) Jeśli jesteś w punkcie, w którym podniesienie wartości IndexError ma znaczny koszt, być może powinieneś zająć się całą tą sprawą w Pythonie.
Joachim Jablon
20
(L[n:n+1] or [somedefault])[0]
Ignacio Vazquez-Abrams
źródło
1
+1, ponieważ dzięki temu dowiedziałem się, co [] or ...zrobiłem. Jednak osobiście po prostu skorzystałbym z przyjętego rozwiązania, ponieważ łatwo się czyta (dla nowicjuszy). To prawda, zawinięcie go w „def” z komentarzem sprawiłoby, że w dużej mierze nie byłby to problem.
ToolmakerSteve
A co, jeśli L[n] == Falselub L[n] == Nonelub L[n] == []bardziej globalnie cokolwiek, co ocenia się jako fałszywe?
Joachim Jablon
@JoachimJablon: Nadal działa. Wycinek zwraca listę i [False]jest prawdziwy.
Ignacio Vazquez-Abrams
Och, nie zdawałem sobie sprawy, że lista została sprawdzona, a nie wartość.
Joachim Jablon
Dlaczego zawijasz to w krotkę? Myślę, że myval = l[n:n+1] or [somedefault]będzie działać dobrze?
Rotareti
8

... szukam odpowiednika dict.get(key, default)list for w pythonie

Istnieją przepisy itertools, które robią to dla ogólnych iterable. Dla wygody możesz > pip install more_itertoolszaimportować tę bibliotekę innej firmy, która implementuje dla Ciebie takie przepisy:

Kod

import more_itertools as mit


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

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

Szczegół

Oto realizacja nthprzepisu:

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

Na przykład dict.get()to narzędzie zwraca wartość domyślną dla brakujących indeksów. Dotyczy to ogólnych iterable:

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
źródło
2

Tanim rozwiązaniem jest naprawdę nagranie z wyliczeniem i użyciem .get()jak zwykle, na przykład

 dict(enumerate(l)).get(7, my_default)
Scorchio
źródło
1

Łącząc @ Joachim z powyższym, możesz użyć

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

Przykłady:

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

A może bardziej przejrzyste, ale bez len

my_list[index] if my_list[index:] else default
serv-inc
źródło
1

Użycie Pythona 3.4 contextlib.suppress(exceptions)do zbudowania getitem()metody podobnej do 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
źródło