Quiero una forma idiomática de encontrar el primer elemento en una lista que coincida con un predicado.
El código actual es bastante feo:
[x for x in seq if predicate(x)][0]
He pensado en cambiarlo a:
from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()
Pero debe haber algo más elegante ... Y sería bueno si devuelve un None
valor en lugar de generar una excepción si no se encuentra una coincidencia.
Sé que podría definir una función como:
def get_first(predicate, seq):
for i in seq:
if predicate(i): return i
return None
Pero es bastante insípido comenzar a llenar el código con funciones de utilidad como esta (y las personas probablemente no notarán que ya están allí, por lo que tienden a repetirse con el tiempo) si hay elementos integrados que ya proporcionan lo mismo.
Respuestas:
Para buscar el primer elemento en una secuencia
seq
que coincida conpredicate
:O (
itertools.ifilter
en Python 2) :Se eleva
StopIteration
si no hay ninguno.Para volver
None
si no hay tal elemento:O:
fuente
next
que se use en lugar de generar la excepción.next()
está disponible desde Python 2.6. Puede leer la página Novedades para familiarizarse rápidamente con las nuevas funciones.seq.find(&method(:predicate))
o métodos aún más concisos, por ejemplo:[1,1,4].find(&:even?)
ifilter
fue renombradofilter
en Python 3.Puede usar una expresión generadora con un valor predeterminado y luego
next
:Aunque para esta línea necesita usar Python> = 2.6.
Este artículo bastante popular discute más este problema: ¿La función de búsqueda en la lista de Python más limpia? .
fuente
No creo que haya nada malo con ninguna de las soluciones que propusiste en tu pregunta.
Sin embargo, en mi propio código, lo implementaría así:
La sintaxis con
()
crea un generador, que es más eficiente que generar toda la lista a la vez con[]
.fuente
[]
usted podría tener problemas si el iterador nunca termina o si sus elementos son difíciles de crear, más tarde se vuelve ...'generator' object has no attribute 'next'
en Python 3.La respuesta de JF Sebastian es muy elegante, pero requiere python 2.6, como señaló Fortran.
Para la versión Python <2.6, aquí está lo mejor que se me ocurre:
Alternativamente, si necesita una lista más tarde (la lista maneja StopIteration), o si necesita más que solo el primero pero aún no todo, puede hacerlo con islice:
ACTUALIZACIÓN: Aunque personalmente estoy usando una función predefinida llamada first () que detecta una StopIteration y devuelve None, aquí hay una posible mejora con respecto al ejemplo anterior: evite usar filter / ifilter:
fuente