Как получить n-й элемент списка Python или значение по умолчанию, если он недоступен

150

Я ищу в Python эквивалент dictionary.get(key, default)для списков. Есть ли какая-то одна идиома лайнера для получения n-го элемента списка или значения по умолчанию, если оно недоступно?

Например, учитывая список myList, который я хотел бы получить myList[0], или 5, если myListэто пустой список.

Спасибо.

user265454
источник

Ответы:

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

Для поддержки отрицательных индексов мы можем использовать:

l[index] if -len(l) <= index < len(l) else default
Gruszczy
источник
3
Однако не работает, если в вашем списке нет имени - например, в понимании списка.
Xiong Chiamiov
9
Вы, кажется, забыли про отрицательный индекс. Например. index == -1000000должен вернуться default.
nodakai
3
@nodakai, хорошее замечание. Иногда меня это кусало. x[index] if 0 <= index < len(x) else defaultбыло бы лучше, если бы indexможно было когда-нибудь быть отрицательным.
Ben Hoyt
3
@nodakai wow - это отличный пример того, почему может быть лучше использовать try / except, чем пытаться правильно закодировать тест, чтобы он никогда не терпел неудачу. Я все еще не люблю полагаться на try / за исключением случая, который, как я знаю, произойдет, но это увеличивает мою готовность его рассмотреть.
ToolmakerSteve
6
@ToolmakerSteve: Ваша формула valid_index()неверна. Отрицательные показатели являются законными в Python - это должно быть -len(l) <= index < len(l).
Тим Пицкер
60
try:
   a = b[n]
except IndexError:
   a = default

Изменить: я удалил проверку TypeError - возможно, лучше позволить вызывающему обработать это.

Тим Пицкер
источник
3
Мне это нравится. Просто оберните его вокруг функции, чтобы вы могли вызвать ее с итерацией в качестве аргумента. Легче читать.
Нуфал Ибрагим
36
(a[n:]+[default])[0]

Это, вероятно, лучше, чем aбольше

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

Это работает, потому что если a[n:]это пустой список, еслиn => len(a)

Вот пример того, как это работает с range(5)

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

И полное выражение

>>> (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
Вы бы написали это в реальном коде без объяснения комментария?
Питер Хансен
1
@Peter Hansen, только если я играл в гольф;) Однако он работает на всех версиях Python. Принятый ответ работает только на 2.5+
Джон Ла Рой
3
Однако он требует создания 3 временных списков и доступа к 2 индексам для выбора элемента.
Joachim Jablon
Хотя я бы никогда не сделал этого в «реальном» коде, это хороший лаконичный однострочный текст, который я могу легко использовать в REPL на Python, так что вы получите мое одобрение.
Cookyt
next(iter(lst[i:i+1]), default)- просто еще одна запись в загадочном уродливом конкурсе однострочных.
jfs
30

Только что обнаружил, что:

next(iter(myList), 5)

iter(l)возвращает итератор myList, next()потребляет первый элемент итератора и вызывает StopIterationошибку, кроме случаев, когда вызывается со значением по умолчанию, как в данном случае, второй аргумент,5

Это работает только тогда, когда вам нужен 1-й элемент, как в вашем примере, но не в тексте вашего вопроса, поэтому ...

Кроме того, ему не нужно создавать временные списки в памяти, и он работает для любого типа итераций, даже если у него нет имени (см. Комментарий Xiong Chiamiov к ответу gruszczy)

Иоахим Яблон
источник
3
В сочетании с другими ответами: next(iter(myList[n:n+1]), 5) теперь он работает для nэлемента th.
Alfe
Это не сработает с не списками. На этом этапе попробуйте: за исключением того, что IndexError читается как лучшая идея ИМХО. Кроме того, он создает список в памяти.
Joachim Jablon
tryВариант не один вкладыш (ASK , запрашиваемый OP). Это работает только для списков, потому что myListэто список, указанный OP (если быть точным, это что-то индексируемое). Создание копии в памяти здесь не требует больших затрат, потому что я создаю здесь список из одного (или ни одного) элемента. Конечно, это небольшие накладные расходы, но не стоит их упоминать, если вы не делаете это миллионы раз в цикле. Кстати, я думаю, что создание и перехват IndexErrorисключения, вероятно, дороже.
Alfe
удобочитаемость имеет значение :) Если вы находитесь в точке, где возникновение IndexError требует значительных затрат, возможно, вам стоит сделать все это на Python.
Joachim
21
(L[n:n+1] or [somedefault])[0]
Игнасио Васкес-Абрамс
источник
1
+1 потому что это заставило меня узнать, что [] or ...сделал. Однако лично я бы просто использовал принятое решение, так как оно легко читается (для новичков). Конечно, если заключить его в «def» с комментарием, это в значительной степени не проблема.
ToolmakerSteve
Что, если L[n] == Falseили L[n] == Noneили L[n] == []или более глобально все, что оценивается как False?
Joachim Jablon
@JoachimJablon: Все еще работает. Срез возвращает список и [False]является истинным.
Игнасио Васкес-Абрамс
Ой, не понял, что список был проверен, а не значение.
Иоахим Яблон
Зачем вы оборачиваете это кортежем? Думаю myval = l[n:n+1] or [somedefault], сработает нормально?
Rotareti 01
8

... ищу в Python эквивалент dict.get(key, default)для списков

Есть рецепты itertools, которые делают это для общих итераций. Для удобства вы можете > pip install more_itertoolsимпортировать и эту стороннюю библиотеку, которая реализует для вас такие рецепты:

Код

import more_itertools as mit


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

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

Деталь

Вот реализация nthрецепта:

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

Мол dict.get(), этот инструмент возвращает значение по умолчанию для отсутствующих индексов. Это применимо к общим итерациям:

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

Дешевое решение - действительно сделать диктант с перечислением и использовать .get()как обычно, например

 dict(enumerate(l)).get(7, my_default)
Скоркио
источник
Это очень медленно запускается, потому что весь список преобразуется в словарь только для того, чтобы получить позже только один элемент.
Arty
1

Комбинируя @ Joachim с вышеперечисленным, вы можете использовать

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

Примеры:

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

Или, может быть, более ясно, но без len

my_list[index] if my_list[index:] else default
serv-inc
источник
1

Использование Python 3.4 contextlib.suppress(exceptions)для создания getitem()метода, аналогичного 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
Харви
источник
0

Прочитав ответы, я буду использовать:

(L[n:] or [somedefault])[0]
Матье Кароф
источник
Вероятно, вы хотите (L[n:n+1] or [somedefault])[0](добавлено n + 1) вместо этого, потому что L[n:]создает длинный список и копирует все элементы от nдо конца. Только чтобы потом получить только один элемент. Пока L[n:n+1]создает список максимум из 1 элемента.
Arty
Я считаю, что выбрал форму, которую даю, потому что мне нужно вычислить n, и я не хочу вычислять ее дважды или вводить переменную. Мои списки короткие. - Также ответ L[n:n+1]уже предоставил Игнасио Васкес-Абрамс
Матье КАРФФ