Eu quero uma maneira idiomática de encontrar o primeiro elemento em uma lista que corresponde a um predicado.
O código atual é bastante feio:
[x for x in seq if predicate(x)][0]
Eu pensei em mudar para:
from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()
Mas deve haver algo mais elegante ... E seria bom se ele retornasse um None
valor em vez de gerar uma exceção se nenhuma correspondência fosse encontrada.
Eu sei que eu poderia apenas definir uma função como:
def get_first(predicate, seq):
for i in seq:
if predicate(i): return i
return None
Mas é bastante insípido começar a preencher o código com funções utilitárias como esta (e as pessoas provavelmente não perceberão que elas já estão lá, então elas tendem a ser repetidas ao longo do tempo) se houver itens incorporados que já forneçam o mesmo.
Respostas:
Para encontrar o primeiro elemento em uma sequência
seq
que corresponde apredicate
:Ou (
itertools.ifilter
no Python 2) :Aumenta
StopIteration
se não houver.Para retornar
None
se não houver esse elemento:Ou:
fonte
next
esse argumento, em vez de gerar a exceção.next()
está disponível desde o Python 2.6 Você pode ler a página O que há de novo para se familiarizar rapidamente com os novos recursos.seq.find(&method(:predicate))
ou ainda mais concisa para os métodos de instância, por exemplo:[1,1,4].find(&:even?)
ifilter
foi renomeado parafilter
no Python 3.Você pode usar uma expressão de gerador com um valor padrão e, em seguida,
next
ela:Embora para essa linha única você precise usar Python> = 2.6.
Este artigo bastante popular discute ainda mais esse problema: Função de busca na lista do Python mais limpa? .
fonte
Não acho que haja algo errado nas soluções que você propôs na sua pergunta.
No meu próprio código, eu o implementaria assim:
A sintaxe com
()
cria um gerador, que é mais eficiente do que gerar toda a lista de uma só vez[]
.fonte
[]
você pode ter problemas se o iterador nunca terminar ou se for difícil criar seus elementos, mais tarde ele'generator' object has no attribute 'next'
em Python 3.A resposta de JF Sebastian é mais elegante, mas requer o python 2.6, como fortran apontou.
Para a versão Python <2.6, aqui está o melhor que posso apresentar:
Como alternativa, se você precisar de uma lista posteriormente (a lista lida com o StopIteration) ou se precisar de mais do que apenas o primeiro, mas ainda não todos, poderá fazê-lo com o islice:
ATUALIZAÇÃO: Embora eu pessoalmente esteja usando uma função predefinida chamada first () que captura uma StopIteration e retorna None, aqui está uma possível melhoria em relação ao exemplo acima: evite usar filter / ifilter:
fonte