@NoPy: 1. Ist ein Generatorausdruck. Also eine „list comprehension” in runden Klammern, wobei man die runden Klammern von einem Funktionsaufruf ”mitbenutzen” kann, solange dadurch keine Mehrdeutigkeiten entstehen. In dem Fall weigert der Compiler sich das ohne extra Klammern zu nehmen, das kann also nicht aus versehen passieren.
Das Ergebnis eines Generatorausdrucks ist „iterierbar”, das heisst man kann über die einzelnen Elemente zum Beispiel mit einer ``for``-Schleife iterieren. Der Begriff „iterierbar” kommt als Eigenschaft von Objekten in Python recht häufig vor. So ein Generator-Objekt garantiert noch ein bisschen mehr, nämlich das es selbst ein Iterator ist. Man kann mit `next()` das jeweils nächste Element abfragen bis eventuell keine mehr da sind und eine `StopIteration`-Ausnahme ausgelöst wird.
Code: Alles auswählen
In [18]: a = (match.group('INHALT').strip() for match in re.finditer(regular_expression, string))
In [19]: a
Out[19]: <generator object <genexpr> at 0xa298644>
In [20]: next(a)
Out[20]: '(Das) 1'
In [21]: next(a)
Out[21]: '(ist) 2'
In [22]: next(a)
Out[22]: '(ein) 3'
In [23]: next(a)
Out[23]: '(Test) 4'
In [24]: next(a)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
/home/bj/<ipython-input-24-3f6e2eea332d> in <module>()
----> 1 next(a)
StopIteration:
2. `set()` erzeugt ein `set()`-Objekt aus jedem beliebigen iterierbaren Objekt das „hash”- und vergleichbare (``==``) Objekte liefert. Listen, zum lesen geöffnete Textdateien, eben alles wo man mit einer ``for``-Schleife drüber gehen kann.
Code: Alles auswählen
In [26]: set(match.group('INHALT').strip() for match in re.finditer(regular_expression, string))
Out[26]: set(['(ist) 2', '(ein) 3', '(Das) 1', '(Test) 4'])
3. `sorted()` erstellt aus jedem beliebigen iterierbaren Objekt das vergleichbare (``<``/``>``) Objekte liefert, also unter anderem auch aus einem `set`-Objekt, eine sortierte Liste.
Code: Alles auswählen
In [27]: sorted(set(match.group('INHALT').strip() for match in re.finditer(regular_expression, string)))
Out[27]: ['(Das) 1', '(Test) 4', '(ein) 3', '(ist) 2']
4. Die `str.join()`-Methode nimmt als Argument jedes beliebige iterierbare Objekt das Zeichenketten liefert und erstellt daraus eine Gesamtzeichenkette mit der Zeichenkette auf der das Aufgerufen wurde als Trenner.
Code: Alles auswählen
In [28]: ', '.join(sorted(set(match.group('INHALT').strip() for match in re.finditer(regular_expression, string))))
Out[28]: '(Das) 1, (Test) 4, (ein) 3, (ist) 2'
Das Iterator-Konzept ist in Python ziemlich zentral. Jede ``for``-Schleife nutzt es und es gibt viele Funktionen die etwas iterierbares als Argument nehmen und/oder als Rückgabewert liefern. Zum Beispiel nehmen fast alle Container-Datentypen ein iterierbares Objekt als Argument beim erzeugen um sich zu füllen. Und Containerdatentypen selbst sind auch iterierbar.
Das mit dem Initialisieren von Variablen am Anfang führt halt gerne mal zu Fehlern, insbesondere wenn man den gleichen Namen öfter verwendet. Was man ja eigentlich auch nicht unbedingt machen sollte. Aber dann denkt man halt man weiss was der Name am Anfang mal zugewiesen bekommen hat, in der Zwischenzeit wurde aber schon mal etwas anderes damit gemacht. Ausserdem vergisst man gerne mal initialisierte Variablen die in der fertigen Fassung gar nicht mehr gebraucht werden. Und es ist schwieriger Teile einer Funktion in eine neue auszulagern wenn nicht alles was zusammengehört auch „räumlich” zusammen steht.
Wie gesagt: Wenn man nicht mehr auseinander halten kann welche Namen als Argumente übergeben wurde, und welche lokal definiert wurden, dann ist die Funktion so lang das man nicht mehr eben schnell in die Signatur schauen kann und es gibt sehr viele Namen. Ein Anzeichen, dass man vielleicht etwas umschreiben sollte. Sehr viele Lauf-, Index-, und Hilfsvariablen die man in „alten” Sprachen brauchte, sind in modernen Programmiersprachen auch gar nicht mehr nötig. Meine Fassung hat neben den beiden Argumenten ja nur noch einen anderen lokalen Namen (`match`).