Gostaria de obter o primeiro item de uma lista que corresponde a uma condição. É importante que o método resultante não processe a lista inteira, que pode ser bastante grande. Por exemplo, a seguinte função é adequada:
def first(the_iterable, condition = lambda x: True):
for i in the_iterable:
if condition(i):
return i
Esta função pode ser usada algo como isto:
>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
No entanto, não consigo pensar em um bom built-in / one-liner para me deixar fazer isso. Eu particularmente não quero copiar esta função, se não for necessário. Existe uma maneira interna de obter o primeiro item correspondente a uma condição?
Respostas:
No Python 2.6 ou mais recente:
Se você deseja
StopIteration
aumentar, se nenhum elemento correspondente for encontrado:Se você deseja
default_value
(por exemploNone
) retornar em vez disso:Observe que você precisa de um par extra de parênteses em torno da expressão do gerador nesse caso - eles são necessários sempre que a expressão do gerador não for o único argumento.
Vejo a maioria das respostas ignorando resolutamente o
next
built-in e, portanto, presumo que, por algum motivo misterioso, eles estejam 100% focados nas versões 2.5 e anteriores - sem mencionar o problema da versão do Python (mas não vejo essa menção em as respostas que fazer mencionar onext
built-in, que é por isso que eu pensei que é necessário dar uma resposta a mim mesmo - pelo menos a questão "versão correta" fica no registro desta forma ;-).Na versão 2.5, o
.next()
método dos iteradores aumenta imediatamenteStopIteration
se o iterador terminar imediatamente - ou seja, para o seu caso de uso, se nenhum item no iterável atender à condição. Se você não se importa (você sabe que deve haver pelo menos um item satisfatório), basta usar.next()
(melhor em um genexp, linha para onext
built-in no Python 2.6 e melhor).Se você fazer o cuidado, as coisas embrulho em uma função como você tinha indicado pela primeira vez em sua Q parece melhor, e enquanto a implementação da função que você propôs é muito bem, você pode usar como alternativa
itertools
, umfor...: break
loop, ou um genexp, outry/except StopIteration
como o corpo da função , como várias respostas sugeridas. Não há muito valor agregado em nenhuma dessas alternativas, então eu usaria a versão extremamente simples que você propôs pela primeira vez.fonte
StopIteration
quando nenhum elemento encontradoStopIteration
isso não é realmente bonito. Melhor usar um método.Como uma função reutilizável, documentada e testada
Versão com argumento padrão
O @zorf sugeriu uma versão dessa função em que você pode ter um valor de retorno predefinido se o iterável estiver vazio ou se não houver itens correspondentes à condição:
fonte
StopIteration
é a exceção canônica "fora dos elementos" em python. Não vejo problema em ser lançado. Eu provavelmente usaria um padrão de "Nenhum", que pode ser passado como um parâmetro padrão para a função.Exceções malditas!
Eu amo essa resposta . No entanto, como
next()
gera umaStopIteration
exceção quando não há itens, eu usaria o seguinte snippet para evitar uma exceção:Por exemplo,
Irá gerar uma
StopIteration
exceção;fonte
Semelhante ao uso
ifilter
, você pode usar uma expressão geradora:Em ambos os casos, você provavelmente quer pegar
StopIteration
, caso nenhum elemento atenda à sua condição.Tecnicamente falando, suponho que você possa fazer algo assim:
Evitaria ter que fazer um
try/except
bloco. Mas isso parece meio obscuro e abusivo para a sintaxe.fonte
for foo in genex: break
é apenas uma maneira de fazerfoo = next(genex)
sem deixar clara a tarefa e com a exceção que seria levantada se a operação não fizesse sentido ser esmagada. Terminar com um código de falha em vez de capturar uma exceção geralmente é uma coisa ruim no Python.A maneira mais eficiente no Python 3 é uma das seguintes (usando um exemplo semelhante):
Com estilo "compreensão" :
AVISO : A expressão também funciona com o Python 2, mas no exemplo é usado
range
que retorna um objeto iterável no Python 3 em vez de uma lista como o Python 2 (se você deseja construir um iterável no Python 2, usexrange
).Observe que a expressão evita construir uma lista na expressão de compreensão
next([i for ...])
, que causaria a criação de uma lista com todos os elementos antes de filtrar os elementos e causaria o processamento de todas as opções, em vez de interromper a iteração uma vezi == 1000
.Com estilo "funcional" :
AVISO : Isso não funciona no Python 2, mesmo substituindo
range
comxrange
due quefilter
cria uma lista em vez de um iterador (ineficiente), e anext
função funciona apenas com iteradores.Valor padrão
Conforme mencionado em outras respostas, você deve adicionar um parâmetro extra à função
next
se desejar evitar uma exceção gerada quando a condição não for atendida."funcional"estilo :
estilo "compreensão" :
Com esse estilo, você precisa cercar a expressão de compreensão
()
para evitarSyntaxError: Generator expression must be parenthesized if not sole argument
:fonte
Eu escreveria isso
fonte
i > 3
deve serx > 3
no seu exemploO
itertools
módulo contém uma função de filtro para iteradores. O primeiro elemento do iterador filtrado pode ser obtido chamandonext()
-o:fonte
i
)filter
e (i
)map
podem fazer sentido para os casos em que as funções aplicadas já existem, mas em uma situação como essa, faz muito mais sentido usar uma expressão geradora.Para versões mais antigas do Python, onde o próximo built-in não existe:
fonte
Usando
pode-se verificar a condição do valor do primeiro item noiterável e obter seu índice sem a necessidade de avaliar todos os itens no the_iterable .
A expressão completa a ser usada é
Aqui first_index assume o valor do primeiro valor identificado na expressão discutida acima.
fonte
Esta pergunta já tem ótimas respostas. Só estou adicionando meus dois centavos porque cheguei aqui tentando encontrar uma solução para o meu próprio problema, que é muito semelhante ao OP.
Se você deseja encontrar o ÍNDICE do primeiro item que corresponde a um critério usando geradores, basta:
fonte
Você também pode usar a
argwhere
função Numpy. Por exemplo:i) Encontre o primeiro "l" em "helloworld":
ii) Encontre o primeiro número aleatório> 0,1
iii) Encontre o último número aleatório> 0,1
fonte
No Python 3:
No Python 2.6:
Edição: Eu pensei que era óbvio, mas aparentemente não: em vez de
None
você pode passar uma função (ou alambda
) com uma verificação para a condição:fonte
Oneliner:
Se você não tiver certeza de que algum elemento será válido de acordo com os critérios, você deve anexá-lo,
try/except
pois isso[0]
pode gerar umIndexError
.fonte