@Ronsen90: Zeile 8 ist ein Generatorausdruck. Der funktioniert im Grunde wie eine „list comprehension” (LC) nur dass er keine komplette Liste mit allen Elementen ergibt, sondern einen Iterator bei dem die Elemente erst ausgewertet werden wenn man danach fragt, also wenn man beispielsweise mit der `next()`-Funktion das nächste Element abfragt, oder in einer Schleife über den Generator itertiert.
Wenn Du da ein ``i for i`` liest, dann ist das nicht weit genug gelesen, denn das ist ja nur der Anfang. Das Komma trennt nicht ``i for i`` vom Rest sondern das Komma steht zwischen `i` und `line`! Wird vielleicht deutlicher wenn man es klammert:
Code: Alles auswählen
(i for (i, line) in enumerate(lines, 1) if line.rstrip() == 'x')
Der Ausdruck liefert alle `i`\s für Zeilen die wenn man Whitespace am Ende entfernt nur noch aus einem 'x' bestehen.
Mal Schritt für Schritt erst mit einer LC damit man potentiell alle Werte sieht wo beim Generator in dem Code nur bis zum ersten Treffer ausgewertet wird. Nehmen wird das Wort 'mississippi' und wollen wissen wo das erste 'i' vorkommt, wobei die Zählung der Buchstaben bei 1 anfangen soll. Wir nummerieren als erstes mal die Buchstaben mit der `enumerate()`-Funkion und brauchen da schon etwas um die Werte auch tatsächlich alle zu erzeugen, denn `enumerate()` liefert auch erst einmal nur einen Iterator und erzeugt die einzelnen Elemente erst wenn man sie abfragt:
Code: Alles auswählen
In [1]: s = 'mississippi'
In [2]: enumerate(s, 1)
Out[2]: <enumerate at 0x93b2324>
In [3]: [(i, c) for i, c in enumerate(s, 1)]
Out[3]:
[(1, 'm'),
(2, 'i'),
(3, 's'),
(4, 's'),
(5, 'i'),
(6, 's'),
(7, 's'),
(8, 'i'),
(9, 'p'),
(10, 'p'),
(11, 'i')]
Jetzt packen wir noch eine Bedingung dazu die anhand des aktuellen Buchstabens in `c` alles ausser 'i' filtert:
Code: Alles auswählen
In [4]: [(i, c) for i, c in enumerate(s, 1) if c == 'i']
Out[4]: [(2, 'i'), (5, 'i'), (8, 'i'), (11, 'i')]
Da uns nur die Stelle interessiert, aber nicht der Buchstabe, denn wir wissen ja dass der immer 'i' ist, lassen wir den Buchstaben weg und bekommen eine Liste mit den Stellen an denen ein 'i' steht:
Code: Alles auswählen
In [5]: [i for i, c in enumerate(s, 1) if c == 'i']
Out[5]: [2, 5, 8, 11]
Jetzt interessiert uns aber gar nicht jedes 'i' sondern nur das erste, darum ist das ungünstig wenn man eine Liste mit *allen* Werten erstellt. Wenn man die eckigen Klammern nun durch runde ersetzt, wird aus der LC ein Generatorausdruck der nicht alle Werte berechnet, sondern erst wenn man danach fragt. Und nach dem nächsten Wert, also beim ersten mal nach dem ersten Wert, kann man mit der `next()`-Funktion fragen:
Code: Alles auswählen
In [6]: next((i for i, c in enumerate(s, 1) if c == 'i'))
Out[6]: 2
Im Programm habe ich dann noch ein zweites Argument an `next()` übergeben. Das ist nötig falls man etwas filtert so dass am Ende gar keine Elemente von dem Generator geliefert werden, zum Beispiel wenn man nur Elemente aus `s` haben will die den Wert 'x' haben, die es aber gar nicht gibt:
Code: Alles auswählen
In [7]: next((i for i, c in enumerate(s, 1) if c == 'x'))
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-7-f71d39b4df0f> in <module>()
----> 1 next((i for i, c in enumerate(s, 1) if c == 'x'))
Statt der Ausnahme hätte ich in diesem Fall gerne den Wert 0, also gebe ich den als Defaultwert für diesen Fall an:
Code: Alles auswählen
In [8]: next((i for i, c in enumerate(s, 1) if c == 'x'), 0)
Out[8]: 0