96 votes

Comment rechercher une liste de tuples en Python

J'ai donc une liste de tuples comme celle-ci :

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

Je veux cette liste pour un tuple dont la valeur numérique est égale à quelque chose.

Donc si je fais search(53) il retournera la valeur de l'index de 2

Y a-t-il un moyen facile de le faire ?

102voto

Ignacio Vazquez-Abrams Points 312628
[i for i, v in enumerate(L) if v[0] == 53]

17 votes

Expliqué en mots : Pour chaque i, v dans une liste énumérée de L (qui fait de i la position de l'élément dans la liste énumérée et de v le tuple original), vérifiez si le premier élément du tuple est 53, si c'est le cas, ajoutez le résultat du code avant 'for' à une liste nouvellement créée, ici : i. Cela pourrait aussi être my_function(i, v) ou encore une autre compréhension de liste. Puisque votre liste de tuples n'a qu'un seul tuple avec 53 comme première valeur, vous obtiendrez une liste avec un seul élément.

8 votes

Je voudrais juste ajouter [i pour i, v dans enumerate(L) if v[0] == 53].pop() pour avoir une valeur int.

54voto

Jon Surrell Points 325

Tl;dr

A expression de générateur est probablement la solution la plus performante et la plus simple à votre problème :

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

Explication

Il existe plusieurs réponses qui fournissent une solution simple à cette question avec des compréhensions de listes. Bien que ces réponses soient parfaitement correctes, elles ne sont pas optimales. En fonction de votre cas d'utilisation, il peut y avoir des avantages significatifs à apporter quelques modifications simples.

Le principal problème que je vois avec l'utilisation d'une compréhension de liste pour ce cas d'utilisation est que la fonction liste complète sera traité, même si vous voulez seulement trouver 1 élément .

Python fournit une construction simple qui est idéale ici. Elle s'appelle le expression de générateur . Voici un exemple :

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

On peut s'attendre à ce que cette méthode fonctionne de la même manière que les compréhensions de listes dans notre exemple trivial, mais que se passe-t-il si nous travaillons avec un ensemble de données plus important ? C'est là que l'avantage d'utiliser la méthode du générateur entre en jeu. Plutôt que de construire une nouvelle liste, nous utiliserons votre liste existante comme itérable, et nous utiliserons la méthode du générateur. next() pour obtenir le premier élément de notre générateur.

Voyons comment ces méthodes se comportent différemment sur certains ensembles de données plus importants. Il s'agit de grandes listes, composées de 10000000 + 1 éléments, avec notre cible au début (le meilleur) ou à la fin (le pire). Nous pouvons vérifier que ces deux listes ont les mêmes performances en utilisant la compréhension de liste suivante :

Compréhensions de listes

"Le pire des cas"

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

"Meilleur cas"

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Expressions du générateur

Voici mon hypothèse pour les générateurs : nous verrons que les générateurs auront des performances nettement meilleures dans le meilleur des cas, mais similaires dans le pire des cas. Ce gain de performance est principalement dû au fait que le générateur est évalué de manière paresseuse, c'est-à-dire qu'il ne calcule que ce qui est nécessaire pour obtenir une valeur.

Le pire des cas

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

Meilleur cas

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

QUOI ? ! Le meilleur cas s'envole mais je ne m'attendais pas à ce que notre pire cas surpasse à ce point les comprehensions de listes. Comment cela se fait-il ? Franchement, je ne peux que spéculer sans faire de recherches supplémentaires.

Prenez tout cela avec un grain de sel, je n'ai pas effectué de profilage robuste ici, juste quelques tests très basiques. Cela devrait suffire pour comprendre qu'une expression de générateur est plus performante pour ce type de recherche de liste.

Notez que tout ceci est du python de base, intégré. Nous n'avons pas besoin d'importer quoi que ce soit ou d'utiliser des bibliothèques.

J'ai vu pour la première fois cette technique de recherche dans le Udacity cs212 cours avec Peter Norvig.

2 votes

Intéressant, j'ai testé et trouvé que c'est vraiment rapide

3 votes

Cela devrait être la réponse acceptée. Les expressions génératrices ne matérialisent pas la séquence de sortie entière lorsqu'elles sont exécutées, mais elles sont évaluées par un itérateur qui produit un élément à la fois de l'expression.

2 votes

C'est génial, beaucoup plus rapide que la compréhension d'une liste dans mon cas, merci !

49voto

Greg Hewgill Points 356191

Vous pouvez utiliser un compréhension de la liste :

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2

31voto

Andrew Jaffe Points 9205

Vos tuples sont fondamentalement des paires clé-valeur - un python dict -- donc :

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

Edit -- aha, vous dites que vous voulez la valeur d'index de (53, "xuxa"). Si c'est vraiment ce que vous voulez, vous devrez itérer dans la liste originale, ou peut-être créer un dictionnaire plus compliqué :

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

3 votes

Si nous ignorons ce que le PO a réellement demandé, je pense que votre réponse initiale est la meilleure réponse à "Comment rechercher une liste de tuples en Python".

0 votes

Votre première réponse était utile pour mes besoins. Il serait peut-être préférable d'utiliser .get(), au cas où l'élément ne serait pas dans le dict. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)

12voto

David Z Points 49476

Hmm... eh bien, le moyen le plus simple qui me vient à l'esprit est de le convertir en un dict...

d = dict(thelist)

et l'accès d[53] .

EDIT : Oups, j'ai mal lu votre question la première fois. Il semble que vous vouliez en fait obtenir l'index où un nombre donné est stocké. Dans ce cas, essayez

dict((t[0], i) for i, t in enumerate(thelist))

au lieu d'un simple dict conversion. Puis d[53] serait de 2.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X