Aujourd’hui, on va se concentrer sur les méthodes équivalentes aux méthodes find et findIndex de JavaScript.
Les exemples que je propose traitent aussi bien des valeurs primitives que des objets.
Équivalent Python de la méthode find() de JavaScript
En Python, vous pouvez utiliser la fonction next() avec une expression générateur pour trouver le premier élément d’une liste qui satisfait une condition.
Exemple avec des valeurs primitives pour l’équivalent de find()
|
|
Dans ce code, None est la valeur par défaut retournée par next().
next() prend deux arguments :
- Un itérateur (ici, une expression génératrice qui renvoie des nombres impairs)
- Une valeur par défaut à renvoyer si l’itérateur est épuisé (c’est-à-dire s’il n’y a plus d’éléments correspondants)
Ainsi, si la liste ne contenait aucun nombre impair — par exemple [2, 4, 6, 8] —, au lieu de lever une exception StopIteration, next() renverrait simplement None, ou toute autre valeur que vous lui fourniriez.
Avec cette liste [2, 4, 6, 7, 8], un nombre impair est trouvé (7), donc None n’est jamais utilisé.
Si vous en oubliez, cela peut entraîner une exception StopIteration.
Exemple d’objet équivalent à find()
|
|
Je tiens à mentionner quelques mises en garde importantes :
-
La valeur par défaut
Nonepeut prêter à confusion. SiNoneest une valeur légitime dans votre liste, vous ne pouvez pas distinguer « introuvable » de «Nonetrouvé ». Une pratique courante consiste à utiliser une sentinelle :1 2 3 4_NOT_FOUND = object() result = next((p for p in people if p["age"] > 100), _NOT_FOUND) if result is _NOT_FOUND: print("Aucune correspondance") -
Les clés manquantes lèvent une exception
KeyError. Si undictde la liste ne contient pas la clé"age", vous obtenez une erreur, et non un saut. Protégez-vous avec.get():1result = next((p for p in people if p.get("age", 0) > 30), None) -
Seule la première correspondance est renvoyée. C’est voulu, mais facile à oublier. Si vous avez besoin de toutes les correspondances, utilisez plutôt une compréhension de liste :
1results = [p for p in people if p["age"] > 30]
Équivalent Python de la fonction findIndex en JavaScript
Pour trouver l’index du premier élément répondant à une condition, utilisez next() avec enumerate().
Exemple simple d’équivalent de findIndex()
|
|
Object Example for findIndex() Equivalent
|
|
Considérations relatives aux performances
Efficacité des expressions génératrices
Les expressions génératrices (par exemple (x for x in numbers if x % 2 != 0)) sont économes en mémoire, car elles produisent des valeurs à la demande plutôt que de créer une nouvelle liste en mémoire, comme ce serait le cas avec les compréhensions de liste.
Cela s’avère particulièrement avantageux lors du traitement de grands ensembles de données.
Pour les ensembles de données de petite à moyenne taille, la différence de performances entre les expressions génératrices et d’autres méthodes (par exemple, les compréhensions de liste) peut être négligeable.
Retourner une valeur le plus tôt possible
Les équivalents de find et findIndex cessent de parcourir la liste dès qu’une correspondance est trouvée.
Cela les rend efficaces pour trouver le premier élément correspondant, car ils ne traitent pas inutilement la liste entière.
Complexité algorithmique
La complexité est de O(n) dans le pire des cas, où n est la longueur de la liste. Cela se produit lorsqu’aucune correspondance n’est trouvée, ce qui nécessite de parcourir l’intégralité de la liste.
Profilage des goulots d’étranglement
Si les performances sont critiques, utilisez des outils de profilage tels que cProfile ou perf pour identifier les goulots d’étranglement.
Quand optimiser
- N’optimisez que si le profilage indique que ces fonctions constituent un goulot d’étranglement.
- Pour les recherches ponctuelles ou les petits ensembles de données, privilégiez la lisibilité du code plutôt que les micro-optimisations.
- Utilisez des générateurs pour les grands ensembles de données où l’efficacité de la mémoire est critique.
Autres mises en garde
En raison de la nature de Python, soyez prudent avec les objets mutables, comme les dictionnaires dans les listes, et assurez-vous que les modifications apportées à ces objets n’affectent pas de manière inattendue les itérations suivantes.
Si vous devez fréquemment rechercher des éléments ou des indices dans de grands ensembles de données, envisagez d’utiliser des structures de données plus avancées, telles que des dictionnaires ou des tables indexées, pour accélérer les recherches.
Par exemple,
-
seta une complexité moyenne deO(1)car il effectue ses tests via des tables de hachage. Utilisez-le lorsque vous avez uniquement besoin de vérifier l’existence d’un élément, et non de stocker des valeurs associées. -
dicta également une recherche de clé moyenne deO(1). Pourquoi ? LesdictPython sont classés par ordre d’insertion (depuis la version 3.7+) et hautement optimisés. Pour les recherches inversées (valeur→clé), maintenez undictinversé. -
collections.defaultdicta la même rechercheO(1)mais auto-initialise les clés manquantes. Il devient utile pour le regroupement/l’indexation :defaultdict(list)construit un index des éléments par une certaine clé en un seul passage. -
sortedcontainers.SortedList/SortedDictest un module tiers, mais c’est la référence absolue pour maintenir un ordre trié avec une complexité deO(log n)pour l’insertion, la suppression et la recherche. Il prend en charge le découpage, l’indexation et les requêtes par plage. Il est beaucoup plus ergonomique que l’utilisation manuelle debisect, un autre module tiers.
Alors, comment choisir la bonne méthode ?
- Besoin de savoir « X est-il présent ? » Privilégiez
set - Besoin de connaître « la valeur de X ? » Privilégiez
dict - Besoin de savoir « quel est l’élément le plus proche de X ? » ou pour des requêtes sur une plage ? Privilégiez
SortedList - Besoin d’une recherche multi-attributs ? Privilégiez des index inversés avec
defaultdict
Le gain le plus important consiste généralement à passer de x in my_list (O[n]) à x in my_set (O[1]). Effectuez un profilage avant de vous tourner vers des solutions plus sophistiquées.
Laissez-moi vous montrer avec un exemple avec set :
|
|
Sur mon ordinateur portable (Intel i3, 20 Go de RAM, disque SSD), j’ai obtenu le résultat suivant :
|
|
Notez que la conversion set elle-même est de complexité O(n), elle n’est donc rentable que lorsque vous effectuez plusieurs recherches sur les mêmes données. Si vous ne vérifiez qu’une seule fois, le balayage de la liste peut suffire.
Mais si vous vérifiez l’appartenance à l’intérieur d’une boucle, la différence est considérable :
|
|
Suivez-moi !
Merci d’avoir lu cet article. Assurez-vous de me suivre sur X, de vous abonner à ma publication Substack et d’ajouter mon blog à vos favoris pour ne pas manquer les prochains articles.
Crédit : Photo de Pixabay sur Pexels.