Je veux un moyen idiomatique de trouver le premier élément d'une liste qui correspond à un prédicat.
Le code actuel est assez moche:
[x for x in seq if predicate(x)][0]
J'ai pensé à le changer pour:
from itertools import dropwhile
dropwhile(lambda x: not predicate(x), seq).next()
Mais il doit y avoir quelque chose de plus élégant ... Et ce serait bien si elle renvoie une valeur None
plutôt que de déclencher une exception si aucune correspondance n'est trouvée.
Je sais que je pourrais juste définir une fonction comme:
def get_first(predicate, seq):
for i in seq:
if predicate(i): return i
return None
Mais il est assez insipide de commencer à remplir le code avec des fonctions utilitaires comme celle-ci (et les gens ne remarqueront probablement pas qu’ils sont déjà présents, ils ont donc tendance à se répéter au fil du temps) s’il existe des solutions intégrées qui fournissent déjà la même chose.
next(x for x in seq if predicate(x))
Il soulève StopIteration
s'il n'y en a pas.
next(ifilter(predicate, seq), None)
retourne None
s'il n'y a pas un tel élément.
Vous pouvez utiliser une expression génératrice avec une valeur par défaut, puis next
it:
next((x for x in seq if predicate(x)), None)
Bien que pour ce one-liner, vous devez utiliser Python> = 2.6.
Cet article plutôt populaire aborde plus en détail ce problème: Cleanest Python fonction de recherche dans la liste? .
Je ne pense pas qu'il y ait quelque chose qui cloche dans les solutions que vous avez proposées dans votre question.
Dans mon propre code, je le mettrais en œuvre comme ceci:
(x for x in seq if predicate(x)).next()
La syntaxe avec ()
crée un générateur, ce qui est plus efficace que de générer toute la liste en même temps avec []
.
La réponse de J.F. Sebastian est très élégante, mais nécessite python 2.6 comme l'a souligné fortran.
Pour Python version <2.6, voici le meilleur de ce que je peux trouver:
from itertools import repeat,ifilter,chain
chain(ifilter(predicate,seq),repeat(None)).next()
Alternativement, si vous aviez besoin d'une liste plus tard (list gère le StopIteration), ou si vous aviez besoin de plus que le premier mais pas du tout, vous pouvez le faire avec islice:
from itertools import islice,ifilter
list(islice(ifilter(predicate,seq),1))
UPDATE: Bien que j'utilise personnellement une fonction prédéfinie, appelée first (), qui intercepte une StopIteration et ne renvoie aucune, voici une amélioration possible par rapport à l'exemple ci-dessus: évitez d'utiliser filter/ifilter:
from itertools import islice,chain
chain((x for x in seq if predicate(x)),repeat(None)).next()