Python: Wie kommt man in einer for-Schleife an den Index?
Die for-Schleife in Python gibt dir die Elemente, nicht ihre Positionen.
Wenn du die Position auch brauchst, gibt es dafür eine eingebaute Funktion —
und einen Umweg im C-Stil, den die meisten zuerst schreiben.
Die Antwort
for i, wert in enumerate(elemente):
...
0 a
1 b
2 c
enumerate
liefert Paare aus (Index, Wert). Es arbeitet verzögert und baut nichts auf.
Bei eins anzufangen ist ein Parameter und keine Rechnung:
enumerate(items, start=1) : [(1, 'a'), (2, 'b'), (3, 'c')]
Dieses start= ist der Teil, den man übersieht — und über den Schleifenrumpf
verstreute i + 1 sind das Ergebnis.
Die Variante, die man zuerst schreibt
for i in range(len(elemente)):
wert = elemente[i]
Das funktioniert. Es greift aber in jedem Durchlauf wieder in die Liste hinein,
was sowohl langsamer als auch lauter ist, als sich den Wert von enumerate
geben zu lassen, das ihn ohnehin schon hatte.
Das stärkere Argument ist die Allgemeinheit. range(len(x)) braucht len(),
und vieles, worüber man iteriert, hat kein len():
enumerate on a generator : [(0, 'x'), (1, 'y')]
len(a generator) -> TypeError: object of type 'generator' has no len()
enumerate funktioniert mit allem Iterierbaren — einem Generator, einer
offenen Datei, einem dict, einem set, einem Datenbank-Cursor.
range(len()) funktioniert nur mit Sequenzen.
Zwei Folgen gleichzeitig
Wenn du zu Indizes gegriffen hast, um zwei Listen parallel zu durchlaufen, ist
die Antwort zip und
nicht der Index:
zip(names, ages) : [('ada', 36), ('alan', 41)]
Und wenn du die Position auch brauchst, lassen sich beide schachteln:
enumerate(zip(names, ages)) : [(0, ('ada', 36)), (1, ('alan', 41))]
Eines sollte man über zip wissen: Es hört bei der kürzeren Folge auf, und
zwar stillschweigend.
zip stops at the shorter one : [(1, 'a'), (2, 'b')]
Seit Python 3.10 kannst du das abstellen, eingeführt mit PEP 618:
zip(..., strict=True) -> ValueError: zip() argument 2 is shorter than argument 1
Wenn die beiden Folgen gleich lang sein sollen, dann sag das auch. Stilles Abschneiden ist ein Fehler, der plausibel aussehende Ergebnisse liefert.
Verändere die Liste nicht, während du sie durchläufst
Das ist der Grund, aus dem viele überhaupt nach dem Index suchen, und man sollte einmal sehen, was dabei passiert:
zahlen = [1, 2, 3, 4, 5, 6]
for i, v in enumerate(zahlen):
if v % 2 == 0:
zahlen.remove(v)
removing while iterating : visited [1, 2, 4, 6], list is now [1, 3, 5]
Das Ergebnis sieht zufällig richtig aus, der Weg dorthin war es nicht. Die
Schleife hat 1, 2, 4, 6 besucht — 3 und 5 hat sie nie gesehen. Jedes
Entfernen schiebt alles nach links, während die Position des Iterators nach
rechts weiterläuft, also wird jedes Mal ein Element übersprungen.
Hier waren die übersprungenen Elemente ungerade, deshalb stimmt die Ausgabe. Ändere die Bedingung und das ist vorbei.
Bau stattdessen eine neue Liste:
zahlen = [v for v in zahlen if v % 2]
build a new list instead : [1, 3, 5]
Über den Index zuzuweisen ist dagegen unproblematisch, weil sich die Länge nicht ändert:
for i, v in enumerate(buchstaben):
buchstaben[i] = v.upper()
assigning through the index works fine : ['A', 'B', 'C']
Ein Detail, das man kennen sollte
enumerate liefert Tupel, und du musst sie nicht auspacken:
list(enumerate('ab')) : [(0, 'a'), (1, 'b')]
without unpacking : (0, 'a')
Das ist gelegentlich nützlich — sorted(enumerate(x), key=...) sortiert die
Werte und behält die ursprünglichen Positionen daran.
Darin liegt auch der Unterschied zwischen diesen beiden:
list(enumerate(reversed(items))) : [(0, 'c'), (1, 'b'), (2, 'a')]
reversed(list(enumerate(items))) : [(2, 'c'), (1, 'b'), (0, 'a')]
Das erste nummeriert bei null neu durch, das zweite behält die ursprünglichen Indizes und läuft rückwärts. Fast immer ist das zweite gemeint.
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, ...).