Je souhaite obtenir le premier élément d'une liste correspondant à une condition. Il est important que la méthode résultante ne traite pas la liste entière, ce qui pourrait être assez volumineux. Par exemple, la fonction suivante est adéquate:
def first(the_iterable, condition = lambda x: True):
for i in the_iterable:
if condition(i):
return i
Cette fonction pourrait être utilisée quelque chose comme ceci:
>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4
Cependant, je ne peux pas penser à un bon intégré / un-doublure pour me permettre de faire cela. Je ne veux pas particulièrement copier cette fonction si je n'ai pas à le faire. Existe-t-il un moyen intégré pour obtenir le premier élément correspondant à une condition?
Réponses:
En Python 2.6 ou plus récent:
Si vous souhaitez
StopIteration
être relevé si aucun élément correspondant n'est trouvé:Si vous souhaitez
default_value
(par exempleNone
) être retourné à la place:Notez que vous avez besoin d'une paire de parenthèses supplémentaires autour de l'expression de générateur dans ce cas - elles sont nécessaires chaque fois que l'expression de générateur n'est pas le seul argument.
Je vois que la plupart des réponses ignorent résolument le
next
intégré et je suppose donc que pour une raison mystérieuse, ils sont 100% concentrés sur les versions 2.5 et antérieures - sans mentionner le problème de la version Python (mais je ne vois pas cette mention dans les réponses qui font mention dunext
, intégré qui est la raison pour laquelle je pensais qu'il est nécessaire de fournir une réponse moi - même - au moins la question « version correcte » obtient enregistrée cette façon ;-).En 2.5, la
.next()
méthode des itérateurs augmente immédiatementStopIteration
si l'itérateur se termine immédiatement - c'est-à-dire, pour votre cas d'utilisation, si aucun élément de l'itérable ne satisfait la condition. Si vous ne vous en souciez pas (c'est-à-dire que vous savez qu'il doit y avoir au moins un élément satisfaisant), alors utilisez-le.next()
(mieux sur un genexp, ligne pour lenext
Python 2.6 intégré et mieux).Si vous vous en souciez, envelopper les choses dans une fonction comme vous l'aviez indiqué dans votre Q semble le mieux, et bien que l'implémentation de la fonction que vous avez proposée soit très bien, vous pouvez également utiliser
itertools
, unefor...: break
boucle, ou un genexp, ou untry/except StopIteration
comme corps de la fonction , comme l'ont suggéré diverses réponses. Il n'y a pas beaucoup de valeur ajoutée dans aucune de ces alternatives, donc j'opterais pour la version simple et simple que vous avez proposée en premier.la source
StopIteration
quand aucun élément n'a été trouvéStopIteration
n'est vraiment pas joli. Mieux vaut utiliser une méthode.En tant que fonction réutilisable, documentée et testée
Version avec argument par défaut
@zorf a suggéré une version de cette fonction où vous pouvez avoir une valeur de retour prédéfinie si l'itérable est vide ou n'a aucun élément correspondant à la condition:
la source
StopIteration
est l'exception canonique "hors éléments" en python. Je ne vois aucun problème à ce qu'il soit lancé. J'utiliserais probablement une valeur par défaut "None" qui peut être transmise comme paramètre par défaut à la fonction.Merde exceptions!
J'adore cette réponse . Cependant, puisque
next()
lever uneStopIteration
exception lorsqu'il n'y a aucun élément, j'utiliserais l'extrait de code suivant pour éviter une exception:Par exemple,
Lève une
StopIteration
exception;la source
Similaire à l'utilisation
ifilter
, vous pouvez utiliser une expression de générateur:Dans les deux cas, vous voudrez probablement attraper
StopIteration
, au cas où aucun élément ne satisferait votre condition.Techniquement parlant, je suppose que vous pourriez faire quelque chose comme ça:
Cela éviterait d'avoir à faire un
try/except
bloc. Mais cela semble un peu obscur et abusif pour la syntaxe.la source
for foo in genex: break
c'est juste une façon de fairefoo = next(genex)
sans clarifier l'affectation et à l'exception qui serait levée si l'opération n'avait pas de sens d'être écrasée. Se retrouver avec un code d'échec au lieu d'attraper une exception est généralement une mauvaise chose en Python.Le moyen le plus efficace en Python 3 est l'un des suivants (en utilisant un exemple similaire):
Avec le style "compréhension" :
AVERTISSEMENT : l'expression fonctionne également avec Python 2, mais dans l'exemple est utilisé
range
qui retourne un objet itérable en Python 3 au lieu d'une liste comme Python 2 (si vous voulez construire un itérable en Python 2, utilisezxrange
plutôt).Notez que l'expression évite de construire une liste dans l'expression de compréhension
next([i for ...])
, ce qui entraînerait la création d'une liste avec tous les éléments avant de filtrer les éléments, et entraînerait le traitement de toutes les options, au lieu d'arrêter l'itération une foisi == 1000
.Avec un style "fonctionnel" :
AVERTISSEMENT : cela ne fonctionne pas en Python 2, même en le remplaçant
range
parxrange
due quifilter
crée une liste au lieu d'un itérateur (inefficace), et lanext
fonction ne fonctionne qu'avec des itérateurs.Valeur par défaut
Comme mentionné dans d'autres réponses, vous devez ajouter un paramètre supplémentaire à la fonction
next
si vous souhaitez éviter une exception déclenchée lorsque la condition n'est pas remplie.style "fonctionnel" :
style "compréhension" :
Avec ce style, vous devez entourer l'expression de compréhension
()
pour éviterSyntaxError: Generator expression must be parenthesized if not sole argument
:la source
J'écrirais ceci
la source
i > 3
devrait êtrex > 3
dans votre exempleLe
itertools
module contient une fonction de filtrage pour les itérateurs. Le premier élément de l'itérateur filtré peut être obtenu en l'appelantnext()
:la source
i
)filter
et (i
)map
peuvent avoir un sens dans les cas où les fonctions appliquées existent déjà, mais dans une situation comme celle-ci, il est beaucoup plus logique d'utiliser une expression de générateur.Pour les anciennes versions de Python où le prochain intégré n'existe pas:
la source
En utilisant
on peut vérifier la condition de la valeur du premier élément dans the_iterable , et obtenir son index sans avoir besoin d'évaluer tous les éléments dans the_iterable .
L'expression complète à utiliser est
Ici, first_index suppose la valeur de la première valeur identifiée dans l'expression discutée ci-dessus.
la source
Cette question a déjà d'excellentes réponses. J'ajoute seulement mes deux cents parce que j'ai atterri ici en essayant de trouver une solution à mon propre problème, qui est très similaire au PO.
Si vous souhaitez trouver l'INDEX du premier élément correspondant à un critère à l'aide de générateurs, vous pouvez simplement faire:
la source
Vous pouvez également utiliser le
argwhere
fonction dans Numpy. Par exemple:i) Trouvez le premier "l" dans "helloworld":
ii) Trouver le premier nombre aléatoire> 0,1
iii) Trouver le dernier nombre aléatoire> 0,1
la source
En Python 3:
En Python 2.6:
EDIT: Je pensais que c'était évident, mais apparemment pas: au lieu de cela,
None
vous pouvez passer une fonction (ou alambda
) avec une vérification de la condition:la source
Bon mot:
Si vous n'êtes pas sûr qu'un élément sera valide selon les critères, vous devez le joindre
try/except
car cela[0]
peut générer unIndexError
.la source