Python: Wie findet man den Index eines Elements in einer Liste?
list.index() beantwortet die Frage mit einem Aufruf. Interessant ist, was
passiert wenn das Element gar nicht da ist, was bei mehrfachem Vorkommen
herauskommt und was der Aufruf bei einer großen Liste kostet.
Die Antwort
farben = ["rot", "gruen", "blau", "gruen"]
farben.index("gruen")
colours.index('green') : 1
Beachte, dass gruen an Index 1 und an Index 3 steht.
list.index()
liefert den ersten Treffer und hört dann auf zu suchen.
Wenn das Element nicht da ist
colours.index('purple') -> ValueError: list.index(x): x not in list
Es wirft. Es liefert nicht -1, wie es
str.find() bei
Strings tut — eine echte Unstimmigkeit in der Standardbibliothek und eine
verlässliche Überraschungsquelle.
Die naheliegende Absicherung ist, vorher zu prüfen:
if "lila" in farben:
i = farben.index("lila")
Das funktioniert und durchläuft die Liste zweimal: einmal für das in, einmal
für das index. Gemessen über 2000 Suchen nach dem letzten Element einer Liste
mit 10.000 Einträgen:
'in' then .index() : 0.2877 s for 2000 runs
try/except around index : 0.1600 s for 2000 runs
ratio : 1.80x
Die pythonische Variante ist hier also zugleich die schnellere:
try:
i = farben.index("lila")
except ValueError:
i = -1
Ein Standardwert ohne try/except
Für einen Einzeiler: ein Generator mit next() und einem Standardwert.
next((i for i, f in enumerate(farben) if f == "lila"), None)
next(generator, None) : None
Diese Form hat einen zweiten Vorteil: Sie sucht nach einer Bedingung, und
das kann .index() überhaupt nicht. Den ersten Datensatz zu finden, der etwas
erfüllt, ist der Fall den man tatsächlich hat:
idx = next((i for i, p in enumerate(personen) if p["alter"] > 40), None)
first person over 40 is at index 1 -> alan
Alle Vorkommen
[i for i, f in enumerate(farben) if f == "gruen"]
[1, 3]
enumerate ist
das richtige Werkzeug, sobald du mehr als den ersten Treffer brauchst.
Willst du nur den nächsten nach einer bekannten Position, nimmt index() die
Argumente start und stop entgegen — dieselben wie ein Slice:
first : 1
second : 3
Verglichen wird mit ==, nicht mit Identität
Das ist wichtig, weil es festlegt, was “gefunden” bedeutet. Hier eine Klasse, die behauptet, allem gleich zu sein:
weird.index('anything') : 0
Praktischer wird es bei float('nan'):
float('nan') in [nan] : True
[float('nan')].index(float('nan')) -> ValueError
Das in mit demselben nan-Objekt gelingt, weil die Enthaltensein-Prüfung
zuerst auf Identität testet. Zwei verschiedene nan-Objekte sind weder
identisch noch gleich — nach IEEE 754 gilt nan != nan — also findet
.index() nichts.
Wann man es sein lässt
.index() sucht von vorne und ist damit O(n). Für eine einzelne Suche ist das
in Ordnung. Für wiederholte Suchen baust du einmal ein Dictionary:
tabelle = {wert: i for i, wert in enumerate(woerter)}
list.index() on 50,000 items : 0.0879 s for 200 lookups
dict lookup : 0.000005 s for 200 lookups
ratio : 17227x
Vier Größenordnungen. Das Dictionary zu bauen kostet einen Durchlauf, jede Suche danach ist praktisch umsonst.
Ist die Liste sortiert, gibt es einen Mittelweg:
bisect ist O(log n) und
braucht keinen zusätzlichen Speicher.
.index() : 0.1681 s bisect : 0.000026 s ratio 6384x
Der Vorbehalt: bisect setzt voraus, dass die Liste sortiert ist, und prüft
das nicht. Bei unsortierter Eingabe liefert es überzeugt ein falsches Ergebnis.
In einer verschachtelten Liste
Dafür gibt es nichts Eingebautes, aber die Comprehension ist kurz genug:
next(((z, s) for z, zeile in enumerate(gitter) for s, w in enumerate(zeile) if w == "d"), None)
position of 'd' in [['a', 'b'], ['c', 'd']] : (1, 1)
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).