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, ...).