Me gustaría obtener el primer elemento de una lista que coincida con una condición. Es importante que el método resultante no procese la lista completa, lo que podría ser bastante grande. Por ejemplo, la siguiente función es adecuada:
def first(the_iterable, condition = lambda x: True):
for i in the_iterable:
if condition(i):
return i
Esta función podría usarse de la siguiente manera:
>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
Sin embargo, no puedo pensar en un buen incorporado / one-liner que me permita hacer esto. En particular, no quiero copiar esta función si no es necesario. ¿Hay una forma integrada de obtener el primer elemento que coincida con una condición?
Respuestas:
En Python 2.6 o más reciente:
Si desea
StopIteration
subir si no se encuentra ningún elemento coincidente:Si desea
default_value
(por ejemploNone
) que se devuelva en su lugar:Tenga en cuenta que necesita un par adicional de paréntesis alrededor de la expresión del generador en este caso; son necesarios siempre que la expresión del generador no sea el único argumento.
Veo que la mayoría de las respuestas ignoran resueltamente el
next
incorporado y, por lo tanto, supongo que por alguna misteriosa razón están 100% enfocadas en las versiones 2.5 y anteriores, sin mencionar el problema de la versión de Python (pero no veo esa mención en las respuestas que hacen mencionan elnext
incorporado, por lo que pensé que es necesario proporcionar una respuesta a mí mismo - por lo menos el tema "versión correcta" se pone en el registro de esta manera ;-).En 2.5, el
.next()
método de iteradores aumenta inmediatamenteStopIteration
si el iterador termina inmediatamente, es decir, para su caso de uso, si ningún elemento en el iterable cumple la condición. Si no te importa (es decir, sabes que hay debe haber al menos un elemento satisfactorio), simplemente use.next()
(mejor en un genexp, línea para elnext
incorporado en Python 2.6 y mejor).Si haces el cuidado, las cosas envolver en una función como se había indicado por primera vez en su Q parece mejor, y mientras que la implementación de la función usted propuso está bien, podría utilizar alternativamente
itertools
, unfor...: break
bucle, o una genexp, o unatry/except StopIteration
como el cuerpo de la función , como lo sugirieron varias respuestas. No hay mucho valor agregado en ninguna de estas alternativas, así que elegiría la versión simple que propusiste por primera vez.fuente
StopIteration
cuando no se encuentra ningún elementoStopIteration
realmente no es bonito. Mejor usa un método.Como una función reutilizable, documentada y probada
Versión con argumento predeterminado
@zorf sugirió una versión de esta función donde puede tener un valor de retorno predefinido si el iterable está vacío o no tiene elementos que coincidan con la condición:
fuente
StopIteration
es la excepción canónica "fuera de elementos" en python. No veo un problema con el lanzamiento. Probablemente usaría un valor predeterminado de "Ninguno" que se puede pasar como parámetro predeterminado a la función.¡Malditas excepciones!
Me encanta esta respuesta . Sin embargo, dado que
next()
genera unaStopIteration
excepción cuando no hay elementos, usaría el siguiente fragmento para evitar una excepción:Por ejemplo,
Levantará una
StopIteration
excepción;fuente
Similar al uso
ifilter
, podría usar una expresión generadora:En cualquier caso, es probable que desee atrapar
StopIteration
, en caso de que ningún elemento satisfaga su condición.Técnicamente hablando, supongo que podrías hacer algo como esto:
Evitaría tener que hacer un
try/except
bloqueo. Pero eso parece un poco oscuro y abusivo de la sintaxis.fuente
for foo in genex: break
es solo una forma de hacerlofoo = next(genex)
sin dejar en claro la tarea y con la excepción de que se plantearía si la operación no tiene sentido ser aplastada. Terminar con un código de falla en lugar de detectar una excepción suele ser algo malo en Python.Las formas más eficientes en Python 3 son una de las siguientes (usando un ejemplo similar):
Con estilo de "comprensión" :
ADVERTENCIA : La expresión también funciona con Python 2, pero en el ejemplo se usa
range
que devuelve un objeto iterable en Python 3 en lugar de una lista como Python 2 (si desea construir un iterable en Python 2 usexrange
en lugar).Tenga en cuenta que la expresión evita construir una lista en la expresión de comprensión
next([i for ...])
, lo que provocaría crear una lista con todos los elementos antes de filtrar los elementos, y provocaría procesar todas las opciones, en lugar de detener la iteración una vezi == 1000
.Con estilo "funcional" :
ADVERTENCIA : Esto no funciona en Python 2, incluso reemplazando
range
conxrange
debido quefilter
crea una lista en lugar de un iterador (ineficiente), y lanext
función solo funciona con iteradores.Valor por defecto
Como se menciona en otras respuestas, debe agregar un parámetro adicional a la función
next
si desea evitar una excepción provocada cuando no se cumple la condición.estilo "funcional" :
"comprensión"estilo de :
Con este estilo necesita rodear la expresión de comprensión
()
para evitarSyntaxError: Generator expression must be parenthesized if not sole argument
:fuente
Yo escribiria esto
fuente
i > 3
debería estarx > 3
en tu ejemploEl
itertools
módulo contiene una función de filtro para iteradores. El primer elemento del iterador filtrado se puede obtener llamandonext()
a él:fuente
i
)filter
y (i
)map
pueden tener sentido para los casos en que las funciones que se aplican ya existen, pero en una situación como esta, tiene mucho más sentido usar una expresión generadora.Para versiones anteriores de Python donde no existe el próximo incorporado:
fuente
Mediante el uso
se puede verificar la condición del valor del primer elemento en the_iterable y obtener su índice sin la necesidad de evaluar todos los elementos en the_iterable .
La expresión completa para usar es
Aquí first_index asume el valor del primer valor identificado en la expresión discutida anteriormente.
fuente
Esta pregunta ya tiene excelentes respuestas. Solo agrego mis dos centavos porque llegué aquí tratando de encontrar una solución a mi propio problema, que es muy similar al OP.
Si desea encontrar el ÍNDICE del primer elemento que coincide con un criterio utilizando generadores, simplemente puede hacer:
fuente
También podría usar la
argwhere
función en Numpy. Por ejemplo:i) Encuentra la primera "l" en "helloworld":
ii) Encuentra el primer número aleatorio> 0.1
iii) Encuentra el último número aleatorio> 0.1
fuente
En Python 3:
En Python 2.6:
EDITAR: pensé que era obvio, pero aparentemente no: en lugar de
None
que pueda pasar una función (o unalambda
) con una verificación de la condición:fuente
Un trazador de líneas:
Si no está seguro de que algún elemento será válido de acuerdo con los criterios, debe adjuntarlo
try/except
ya que[0]
puede generar unIndexError
.fuente