Mam więc listę krotek, takich jak ta:
[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
Chcę mieć tę listę dla krotki, której wartość liczbowa jest równa czemuś.
Więc jeśli to zrobię search(53)
, zwróci wartość indeksu2
Czy jest na to łatwy sposób?
tl; dr
Wyrażenie generator jest prawdopodobnie najbardziej wydajnych i proste rozwiązanie problemu:
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] result = next((i for i, v in enumerate(l) if v[0] == 53), None) # 2
Wyjaśnienie
Istnieje kilka odpowiedzi, które zapewniają proste rozwiązanie tego pytania ze zrozumieniem listy. Chociaż te odpowiedzi są całkowicie poprawne, nie są optymalne. W zależności od przypadku użycia wprowadzenie kilku prostych modyfikacji może przynieść znaczące korzyści.
Główny problem, jaki widzę przy używaniu funkcji rozumienia listy w tym przypadku użycia, polega na tym, że cała lista zostanie przetworzona, chociaż chcesz znaleźć tylko 1 element .
Python zapewnia prostą konstrukcję, która jest tutaj idealna. Nazywa się to wyrażeniem generatora . Oto przykład:
# 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)
Możemy oczekiwać, że ta metoda będzie działać w zasadzie tak samo jak składanie list w naszym trywialnym przykładzie, ale co, jeśli pracujemy z większym zestawem danych? W tym miejscu pojawia się zaleta korzystania z metody generatora. Zamiast tworzyć nową listę, użyjemy istniejącej listy jako naszej iterowalnej i użyjemy
next()
do pobrania pierwszego elementu z naszego generatora.Przyjrzyjmy się, jak te metody działają inaczej na niektórych większych zbiorach danych. Są to duże listy, składające się z 10000000 + 1 elementów, z naszym celem na początku (najlepsza) lub na końcu (najgorsza). Możemy sprawdzić, czy obie te listy będą działać jednakowo, korzystając z następującego rozumienia list:
Lista zdań
"Najgorszy przypadek"
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}
„Najlepszy przypadek”
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}
Wyrażenia generatora
Oto moja hipoteza dotycząca generatorów: zobaczymy, że generatory będą działać znacznie lepiej w najlepszym przypadku, ale podobnie w najgorszym. Ten wzrost wydajności wynika głównie z faktu, że generator jest oceniany leniwie, co oznacza, że oblicza tylko to, co jest wymagane do uzyskania wartości.
Najgorszy przypadek
# 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}
Najlepszy przypadek
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}
CO?! Najlepszy przypadek niszczy listy składane, ale nie spodziewałem się, że nasz najgorszy przypadek będzie lepszy od rozumienia list w takim stopniu. W jaki sposób? Szczerze mówiąc, mogłem tylko spekulować bez dalszych badań.
Weź to wszystko z przymrużeniem oka, nie przeprowadziłem tutaj żadnego solidnego profilowania, tylko kilka bardzo podstawowych testów. Powinno to wystarczyć, aby zdać sobie sprawę, że wyrażenie generatora jest bardziej wydajne dla tego typu wyszukiwania list.
Zauważ, że to wszystko jest podstawowym, wbudowanym Pythonem. Nie musimy niczego importować ani używać żadnych bibliotek.
Po raz pierwszy zobaczyłem tę technikę wyszukiwania na kursie Udacity cs212 z Peterem Norvigiem.
źródło
Możesz użyć rozumienia listy :
>>> 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
źródło
Twoje krotki to w zasadzie pary klucz-wartość - python -
dict
tak:l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l)[53]
Edycja - aha, mówisz, że chcesz mieć wartość indeksu (53, "xuxa"). Jeśli naprawdę tego chcesz, będziesz musiał przejrzeć oryginalną listę lub stworzyć bardziej skomplikowany słownik:
d = dict((n,i) for (i,n) in enumerate(e[0] for e in l)) idx = d[53]
źródło
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
Hmm ... cóż, najprostszym sposobem, który przychodzi na myśl, jest przekształcenie tego w dyktando
i dostęp
d[53]
.EDYCJA : Ups, za pierwszym razem źle odczytałem pytanie. Wygląda na to, że faktycznie chcesz uzyskać indeks, w którym przechowywany jest podany numer. W takim razie spróbuj
dict((t[0], i) for i, t in enumerate(thelist))
zamiast zwykłej starej
dict
konwersji. Wtedyd[53]
byłoby 2.źródło
Zakładając, że lista może być długa, a liczby mogą się powtarzać, rozważ użycie typu SortedList z modułu sortcontainers w języku Python . Typ SortedList automatycznie utrzyma krotki w kolejności według numeru i umożliwi szybkie wyszukiwanie.
Na przykład:
from sortedcontainers import SortedList sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]) # Get the index of 53: index = sl.bisect((53,)) # With the index, get the tuple: tup = sl[index]
Będzie to działać znacznie szybciej niż sugestia dotycząca zrozumienia listy, wykonując wyszukiwanie binarne. Sugestia ze słownika będzie jeszcze szybsza, ale nie będzie działać, jeśli mogą istnieć zduplikowane liczby z różnymi ciągami.
Jeśli istnieją zduplikowane liczby z różnymi ciągami, musisz zrobić jeszcze jeden krok:
end = sl.bisect((53 + 1,)) results = sl[index:end]
Dzieląc się na 54, znajdziemy indeks końcowy dla naszego wycinka. Będzie to znacznie szybsze na długich listach w porównaniu z zaakceptowaną odpowiedzią.
źródło
Po prostu inny sposób.
zip(*a)[0].index(53)
źródło
[k dla k, v in l if v == ' delicia ']
oto lista krotek - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]
Zamiast konwertować to na dykt, używamy llist rozumienia.
*Key* in Key,Value in list, where value = **delicia**
źródło