Como obter o enésimo elemento de uma lista python ou um padrão, se não estiver disponível

144

Estou procurando um equivalente em python de dictionary.get(key, default)para listas. Existe algum idioma de liner para obter o enésimo elemento de uma lista ou um valor padrão, se não estiver disponível?

Por exemplo, dada uma lista myList que eu gostaria de obter myList[0], ou 5 se myListfor uma lista vazia.

Obrigado.

user265454
fonte

Respostas:

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

Para suportar índices negativos, podemos usar:

l[index] if -len(l) <= index < len(l) else default
gruszczy
fonte
3
Porém, não funciona se sua lista não tiver um nome - por exemplo, na compreensão da lista.
Xiong Chiamiov
9
Você parece ter esquecido o caso do índice negativo. Por exemplo. index == -1000000deve retornar default.
nodakai
3
@nodakai, bom ponto. Eu às vezes fui mordido por isso. x[index] if 0 <= index < len(x) else defaultseria melhor se indexpudesse ser negativo.
Ben Hoyt
3
@nodakai wow - esse é um ótimo exemplo de por que pode ser melhor usar try / except do que tentar codificar corretamente o teste para que nunca falhe. Ainda não gosto de confiar em try / except, para um caso que sei que acontecerá, mas isso aumenta minha disposição de considerá-lo.
usar o seguinte
6
@ ToolmakerSteve: sua fórmula para valid_index()está incorreta. Índices negativos são legais em Python - deveria ser -len(l) <= index < len(l).
precisa saber é o seguinte
57
try:
   a = b[n]
except IndexError:
   a = default

Edit: Eu removi a verificação para TypeError - provavelmente melhor deixar o chamador lidar com isso.

Tim Pietzcker
fonte
3
Eu gosto disso. Apenas envolva-o em uma função para que você possa chamá-lo com um iterável como argumento. Mais fácil de ler.
Noufal Ibrahim 22/03
35
(a[n:]+[default])[0]

Provavelmente é melhor à medida que aaumenta

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

Isso funciona porque if a[n:]é uma lista vazia sen => len(a)

Aqui está um exemplo de como isso funciona com range(5)

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

E a expressão 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
fonte
5
Você escreveria isso no código real sem um comentário para explicá-lo?
Peter Hansen
1
@ Peter Hansen, apenas se eu estivesse jogando golfe;) No entanto, ele funciona em todas as versões do Python. A resposta Aceita só funciona em 2.5+
John La Rooy 22/03
3
Invlora a criação de 3 listas temporárias e o acesso a 2 índices para selecionar um item.
Joachim Jablon
Embora eu nunca faça isso em código "real", é uma linha concisa agradável e agradável que eu posso usar facilmente em um REPL python, para que você receba meu voto positivo.
Cookyt 21/02
next(iter(lst[i:i+1]), default)- apenas mais uma entrada na competição de uma linha feia e enigmática.
JFS
28

Acabei de descobrir que:

next(iter(myList), 5)

iter(l)retorna um iterador myList, next()consome o primeiro elemento do iterador e gera um StopIterationerro, exceto se chamado com um valor padrão, que é o caso aqui, o segundo argumento,5

Isso só funciona quando você deseja o primeiro elemento, como é o caso no seu exemplo, mas não no texto da sua pergunta, então ...

Além disso, ele não precisa criar listas temporárias na memória e funciona para qualquer tipo de iterável, mesmo que não tenha um nome (consulte o comentário de Xiong Chiamiov na resposta de gruszczy)

Joachim Jablon
fonte
3
Combinado com as outras respostas: next(iter(myList[n:n+1]), 5) Agora ele funciona para o nelemento th.
Alfe
Isso não funcionaria com não listas. Neste ponto, tente: exceto que um IndexError parece uma idéia melhor do IMHO. Além disso, ele cria uma lista na memória.
Joachim Jablon
A tryvariante não é uma linha (peça solicitada pelo OP). Funciona apenas para listas porque myListé uma lista especificada pelo OP (para ser mais preciso, é algo indexável). Criar uma cópia na memória não é caro aqui, porque estou criando uma lista de um único elemento (ou nenhum) aqui. Claro, um pouco acima, mas não vale a pena mencionar, a menos que você faça isso milhões de vezes seguidas. Btw, eu acho que criar e capturar uma IndexErrorexceção é provavelmente mais caro.
Alfe
a legibilidade conta :) Se você está no ponto em que aumentar um IndexError tem um custo significativo, talvez você deva fazer a coisa toda em Python.
Joachim Jablon 12/01/19
20
(L[n:n+1] or [somedefault])[0]
Ignacio Vazquez-Abrams
fonte
1
+1 porque isso me fez aprender o que [] or ...fez. No entanto, eu pessoalmente usaria apenas a solução aceita, pois ela lê facilmente (para iniciantes). Concedido, envolvê-lo em um 'def' com comentários tornaria isso amplamente um problema.
Página Inicial>
E se L[n] == Falseou L[n] == Noneou L[n] == []ou mais globalmente qualquer coisa que avalia a falsa?
Joachim Jablon
@JoachimJablon: Ainda funciona. A fatia retorna uma lista e [False]é verdadeira.
Ignacio Vazquez-Abrams
Ah, não percebi que a lista estava checada e não o valor, de fato.
Joachim Jablon
Por que você quebra isso em uma tupla? Eu acho que myval = l[n:n+1] or [somedefault]iria funcionar muito bem?
Rotareti
8

... procurando um equivalente em python de dict.get(key, default)para listas

Existem receitas de ferramentas que fazem isso para iteráveis ​​gerais. Por conveniência, você pode > pip install more_itertoolsimportar esta biblioteca de terceiros que implementa essas receitas para você:

Código

import more_itertools as mit


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

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

Detalhe

Aqui está a implementação da nthreceita:

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

Assim dict.get(), essa ferramenta retorna um padrão para os índices ausentes. Aplica-se a iterables gerais:

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
fonte
2

Uma solução barata é realmente fazer um ditado com enumerar e usar .get()como de costume, como

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

Combinando o @ Joachim's com o acima, você pode usar

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

Exemplos:

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

Ou, talvez mais claro, mas sem o len

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

Usando o Python 3.4 contextlib.suppress(exceptions)para criar um getitem()método semelhante ao 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
fonte