Je voudrais obtenir le premier élément d'une liste correspondant à une condition. Il est important que la méthode résultante ne traite pas la totalité de la liste, qui pourrait être assez longue. 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 à une bonne ligne intégrée/une ligne pour me permettre de le faire. Je ne veux pas particulièrement copier cette fonction si je n'ai pas à le faire. Existe-t-il un moyen intégré d’obtenir le premier élément correspondant à une condition?
Dans Python 2.6 ou mieux:
Si vous souhaitez que StopIteration
soit déclenché si aucun élément correspondant n'est trouvé:
next(x for x in the_iterable if x > 3)
Si vous souhaitez que default_value
(par exemple, None
) soit renvoyé à la place:
next( (x for x in the_iterable if x>3), default_value)
Notez que dans ce cas, vous avez besoin d'une paire de parenthèses supplémentaire autour de l'expression du générateur. Elles sont toujours nécessaires lorsque l'expression du 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 se concentrent à 100% sur les versions 2.5 et antérieures - sans mentionner le problème de la version Python (mais ensuite Je ne vois pas cette mention dans les réponses que fait mentionne le next
intégré, raison pour laquelle j’ai jugé nécessaire de fournir moi-même une réponse - du moins le "correct" Le numéro "version" est enregistré de cette façon ;-).
Dans la version 2.5, la méthode .next()
des itérateurs soulève immédiatement StopIteration
si l'itérateur se termine immédiatement - c'est-à-dire, pour votre cas d'utilisation, si aucun élément de l'itéré ne remplit la condition. Si vous ne vous en souciez pas (c’est-à-dire que vous savez que il faut avoir au moins un élément satisfaisant), utilisez simplement .next()
(mieux sur un genexp, ligne pour next
intégré dans Python 2.6 et supérieur).
Si vous soignez , encapsuler les objets dans une fonction comme indiqué dans votre Q semble meilleur, et bien que l'implémentation de la fonction que vous proposez fonctionne parfaitement, vous pouvez également utiliser itertools
, une boucle for...: break
, ou un genexp, ou un try/except StopIteration
en tant que corps de la fonction, comme suggéré par diverses réponses. Aucune de ces solutions n'apportant une valeur ajoutée importante, je choisirais donc la version simplissime que vous avez proposée en premier.
def first(iterable, condition = lambda x: True):
"""
Returns the first item in the `iterable` that
satisfies the `condition`.
If the condition is not given, returns the first item of
the iterable.
Raises `StopIteration` if no item satysfing the condition is found.
>>> first( (1,2,3), condition=lambda x: x % 2 == 0)
2
>>> first(range(3, 100))
3
>>> first( () )
Traceback (most recent call last):
...
StopIteration
"""
return next(x for x in iterable if condition(x))
Semblable à utiliser ifilter
, vous pouvez utiliser une expression génératrice:
>>> (x for x in xrange(10) if x > 5).next()
6
Dans les deux cas, vous voudrez probablement attraper StopIteration
cependant, au cas où aucun élément ne satisferait votre condition.
Techniquement, je suppose que vous pourriez faire quelque chose comme ceci:
>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
...
>>> foo
6
Cela éviterait de devoir créer un bloc try/except
. Mais cela semble assez obscur et abusif pour la syntaxe.
J'aime cette réponse . Cependant, étant donné que next()
lève une exception StopIteration
quand il n'y a aucun élément, J'utiliserais l'extrait de code suivant pour éviter une exception:
a = []
item = next((x for x in a), None)
Par exemple,
a = []
item = next(x for x in a)
Lève une exception StopIteration
;
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Pour les anciennes versions de Python où la prochaine version intégrée n'existe pas:
(x for x in range(10) if x > 3).next()
J'écrirais ceci
next(x for x in xrange(10) if x > 3)
Le module itertools
contient une fonction de filtrage pour les itérateurs. Le premier élément de l'itérateur filtré peut être obtenu en appelant next()
dessus:
from itertools import ifilter
print ifilter((lambda i: i > 3), range(10)).next()
En utilisant
(index for index, value in enumerate(the_iterable) if condition(value))
on peut vérifier le condition du value du premier élément de the_iterable et obtenir son index sans avoir à évaluer tous les éléments de the_iterable .
L’expression complète à utiliser est
first_index = next(index for index, value in enumerate(the_iterable) if condition(value))
Ici first_index suppose la valeur de la première valeur identifiée dans l'expression décrite ci-dessus.
Le moyen le plus efficace dans Python 3 est l’un des suivants (en utilisant un exemple similaire):
next(i for i in range(100000000) if i == 1000)
WARNING: L'expression fonctionne également avec Python 2, mais dans l'exemple, on utilise range
qui renvoie un objet itérable dans Python 3 au lieu d'une liste comme Python 2 (si vous voulez construire un itérable dans Python 2, utilisez xrange
à la place).
Notez que l'expression évite de construire une liste dans l'expression de compréhension next([i for ...])
, ce qui aurait pour effet de créer une liste avec tous les éléments avant de filtrer les éléments et de traiter toutes les options au lieu d'arrêter l'itération une fois i == 1000
.
next(filter(lambda i: i == 1000, range(100000000)))
WARNING: Cela ne fonctionne pas dans Python 2, même en remplaçant range
par xrange
car filter
crée une liste au lieu d'un itérateur (inefficient), et la fonction next
ne fonctionne qu'avec des itérateurs.
Comme indiqué 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.
next(filter(lambda i: i == 1000, range(100000000)), False)
Avec ce style, vous devez entourer l'expression de compréhension avec ()
pour éviter un SyntaxError: Generator expression must be parenthesized if not sole argument
:
next((i for i in range(100000000) if i == 1000), False)
Cette question a déjà de bonnes réponses. J'ajoute seulement mes deux sous 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 voulez trouver l'INDEX du premier article correspondant à un critère en utilisant des générateurs, vous pouvez simplement faire:
next(index for index, value in enumerate(iterable) if condition)
Étant donné que vous avez demandé une ligne intégrée, cela évitera le problème d'une exception StopIteration
, bien que cela nécessite que votre valeur itérable soit petite afin que vous puissiez la transtyper vers une liste, car c'est la seule construction que je connaisse. avalera un StopIteration et vous permettra de jeter un coup d’œil sur les valeurs:
(lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION))
(Si aucun élément ne correspond, vous obtiendrez None
plutôt qu'une exception StopIteration
.)
Vous pouvez également utiliser la fonction argwhere
dans Numpy. Par exemple:
i) Trouvez le premier "l" dans "helloworld":
import numpy as np
l = list("helloworld") # Create list
i = np.argwhere(np.array(l)=="l") # i = array([[2],[3],[8]])
index_of_first = i.min()
ii) Trouver le premier nombre aléatoire> 0,1
import numpy as np
r = np.random.Rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_first = i.min()
iii) Trouver le dernier nombre aléatoire> 0,1
import numpy as np
r = np.random.Rand(50) # Create random numbers
i = np.argwhere(r>0.1)
index_of_last = i.max()