Zeilen in einer Datei löschen über List-Comprehension

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

@sfx2k: Hier macht das `set` nun aber wieder keinen Sinn mehr. Da wäre eine Liste angebrachter weil das eine einfacherere Datenstruktur ist und Du von der Funktionalität vom `set` nichts verwendest. ;-)
BlackJack

Noch eine Variante das Ganze aufzuteilen. Eine Funktion zum Lesen, eine die eine einzelne Zeile prüft, und eine zum Schreiben des Ergebnis, und dann noch eine Hauptfunktion welche die drei Teile kombiniert:

Code: Alles auswählen

#!/usr/bin/env python
import re
from itertools import ifilterfalse


def iter_lines(filename):
    with open(filename, 'r') as lines:
        for line in lines:
            yield line


def create_line_test(words):
    return re.compile(
        r'\b({0})\b'.format('|'.join(re.escape(word) for word in words)),
        re.IGNORECASE
    ).search


def save_lines(filename, lines):
    with open(filename, 'w') as out_file:
        out_file.writelines(lines)


def main():
    to_delete = ['ich', 'du', 'er', 'sie', 'es']
    save_lines(
        'test2.txt',
        ifilterfalse(create_line_test(to_delete), iter_lines('test.txt'))
    )


if __name__ == '__main__':
    main()
sfx2k
User
Beiträge: 54
Registriert: Dienstag 2. September 2014, 13:29

Sirius3 hat geschrieben:

Code: Alles auswählen

def some_filter(line, args):
    return not re.search(r'\b({})\b'.format('|'.join(map(re.escape, args))), line. re.I)
Ha, das gefällt mir ausgezeichnet. Besonders die Möglichkeit, automatisch escapen zu lassen :)
BlackJack hat geschrieben:@sfx2k: Hier macht das `set` nun aber wieder keinen Sinn mehr. Da wäre eine Liste angebrachter weil das eine einfacherere Datenstruktur ist und Du von der Funktionalität vom `set` nichts verwendest. ;-)
Ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhh :wink:
Okay, dann erkläre mir doch bitte, warum drei Posts vorher laut nezzcarth noch ein Set besser geeignet war als eine Liste, da ich deren Funktionalitäten gar nicht benötige, und jetzt genau andersrum? :?

Bezüglich Deines Quelltextes:
In Delphi hätte ich alles einzeln gemacht. Diese vielen verschachtelten Aufrufe - da muss ich erstmal mit klarkommen.
Besonders das ifilterfalse ist mir noch nicht ganz klar. Naja - mal das Doc dazu lesen :)

Sirius3 benutzt zum Zusammensetzen des Regulären Ausdrucks die map() Funktion, Du eine for-Schleife.
Gibt es da für diesen Fall signifikante Unterschiede? Oder ist das einfach nur Geschmackssache?

Und dann noch eine grundlegende Frage, auf die ich aber keine Antwort finden konnte:
Muss ich, wenn ein File in einer with-Anweisung geöffnet wird, dieses nicht auch wieder schließen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sfx2k hat geschrieben: Ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhh :wink:
Okay, dann erkläre mir doch bitte, warum drei Posts vorher laut nezzcarth noch ein Set besser geeignet war als eine Liste, da ich deren Funktionalitäten gar nicht benötige, und jetzt genau andersrum? :?
Weil Du da noch *ohne* reguläre Ausdrücke geprüft hast, ob ein Wort in einer *Menge* vorkommt. Du hattest eine Liste genommen, die beim ``in`` Operator jedes Mal schlimmsten Falls komplett durchlaufen werden muss (das beschreibt man auch mit O(n)). Bei einer Menge kann der ``in`` Operator in *konstanter* Zeit (O(1)) ermitteln, ob ein Element in einer Liste ist.

Das kann man in einer Python-Shell (hier iPython) leicht nachprüfen:

Code: Alles auswählen

In [1]: values = list(range(10000000))

In [3]: some_value = 9999999

In [4]: timeit some_value in values
1 loops, best of 3: 305 ms per loop

In [5]: values = set(range(10000000))

In [6]: timeit some_value in values
10000000 loops, best of 3: 147 ns per loop
Beachte die Dauer - Millisekunden vs Nanosekunden! Meine Testzahl ist natürlich so schlecht wie möglich für die Liste gewählt, da sie dort die *letzte* Position hat. ``in`` muss also *alle* Einträge durchlaufen, bevor er auf die gewünschte Zahl trifft.

Auch eine nicht vorhandene Zahl muss natürlich immer alle Elemente durchlaufen.

Bei Sammlungen, die auf Hashing basieren (Sets, Dictionaries), kann dies *unabhängig* von der Größe der Sammlung immer in konstanter Zeit erreicht werden.

Edit: Noch einmal zur Verdeutlichung eine Reduktion der Anzahl um den Faktor 10 also von 10 Millionen auf eine Million Elemente. Man erkennt schön, wie der Listen basierte Zugriff sich *linear* um den Faktor reduziert (30ms vs 300ms), wohingegen der Mengen basierte Ansatz gleich geblieben ist:

Code: Alles auswählen

In [14]: some_value = 999999

In [15]: values = list(range(1000000))

In [16]: timeit some_value in values
10 loops, best of 3: 30.6 ms per loop

In [17]: values = set(range(1000000))

In [18]: timeit some_value in values
10000000 loops, best of 3: 145 ns per loop
sfx2k hat geschrieben: Muss ich, wenn ein File in einer with-Anweisung geöffnet wird, dieses nicht auch wieder schließen?
Nein, eben nicht! :-) (Das ist ja das tolle; das passiert übrigens auch bei Ausnahmen!)
Zuletzt geändert von Hyperion am Donnerstag 18. September 2014, 09:01, insgesamt 1-mal geändert.
Grund: Vergleichswerte ergänzt
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@sfx2k: Drei Posts vorher hat ein `set` noch etwas gebracht weil der ``in``-Operator bei Listen die Liste linear, Element für Element, vergleicht, und ein `set()` diese Anfrage ”sofort” beantworten kann, ohne sich alle enthaltenen Element einzeln anschauen zu müssen. Wenn man nur über die Elemente itertiert, dann verwendet man diese Eigenschaft von `set` nicht. Dann könnte man nur noch argumentieren, dass man sicherstellen möchte, dass die Testworte alle unterschiedlich sind, was man bei den wenigen Worten noch ganz gut ohne Unterstützung vom Programm hinbekommt.

Man kann es auch unverschachtelt(er) schreiben, wenn man sich Namen für die Zwischenergebnisse ausdenkt.

Code: Alles auswählen

def main():
    to_delete = ['ich', 'du', 'er', 'sie', 'es']
    lines = iter_lines('test.txt')
    filtered_lines = ifilterfalse(create_line_test(to_delete), lines)
    save_lines('test2.txt', filtered_lines)
Da gute Namen finden, in der Regel schwieriger ist als den Code an sich zu schreiben, spare ich mir das gerne. :-)

Bei den regülären Ausdrücken benutze ich einen Generatorausdruck. Da hätte ich in der Tat `map()` oder `itertools.imap()` verwenden können.

Die ``with``-Anweisung sorgt dafür das egal wo und wie der Programmfluss den Block verlässt, die `__exit__()`-Methode auf dem Objekt aufgerufen wird das nach dem ``with``-Schlüsselwort erzeugt wird. Also in diesem Fall die Datei. Und bei Dateien ist eine `__exit__()`-Methode implementiert, welche die Datei schliesst. Das ist aber auch *das* Beispiel für die ``with``-Anweisung. Da sollte man eigentlich etwas zu finden.
sfx2k
User
Beiträge: 54
Registriert: Dienstag 2. September 2014, 13:29

@Hyperion und Blackjack:
Vielen Dank für diese detaillierten Erläuterungen. Das muss man aber auch erstmal wissen :shock:
Naja, Übung macht den Meister :)

@Blackjack:
Ich fürchte, dass ich im Suchen nach Python-Problemen noch keine Routine habe, also oft gar nicht weiß, nach was und wo ich suchen soll.
In diesem Fall habe ich bei Google nach 'python with file open close' gesucht.
Dabei habe ich mir sogar das erste Suchergebnis angesehen, aufgrund der vielen 'literalen' with im Text aber den Überblick verloren und die wesentliche Stelle einfach übersehen.
It is good practice to use the with keyword when dealing with file objects. This has the advantage that the file is properly closed after its suite finishes, even if an exception is raised on the way.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sfx2k hat geschrieben:@Hyperion und Blackjack:
Vielen Dank für diese detaillierten Erläuterungen. Das muss man aber auch erstmal wissen :shock:
Naja, Übung macht den Meister :)
Naja, lernen ist ja nie verkehrt :-) Hier kannst Du über das Konzept der Zeitkomplexität ein wenig nachlesen. Und hier gibt es einen Überblick über die Zeitkomplexität der Operationen bei den Standard-Containern von Python.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@sfx2k: Ich bin immer wieder erstaunt wie schnell die Leute heute zu Google greifen statt erst einmal in der jeweiligen Dokumentation zu schauen. :-) ``with`` ist in der Python-Dokumentation. Beide Stellen die im Index verlinkt sind verweisen auch am Ende des Abschnitts auf das PEP mit der Spezifikation, Hintergründen, und Beispielen.

Wenn man Schlüsselwörter der Sprache oder etwas aus der Standardbibliothek sucht, dann ist der Index in der Python-Dokumentation sehr praktisch. Der ist auf fast allen Dokumentationsseiten oben rechts zu erreichen.
sfx2k
User
Beiträge: 54
Registriert: Dienstag 2. September 2014, 13:29

Hyperion hat geschrieben: Naja, lernen ist ja nie verkehrt :-) Hier kannst Du über das Konzept der Zeitkomplexität ein wenig nachlesen. Und hier gibt es einen Überblick über die Zeitkomplexität der Operationen bei den Standard-Containern von Python.
Background ist immer gut :) Danke!
BlackJack hat geschrieben:@sfx2k: Ich bin immer wieder erstaunt wie schnell die Leute heute zu Google greifen statt erst einmal in der jeweiligen Dokumentation zu schauen. :-) ``with`` ist in der Python-Dokumentation. Beide Stellen die im Index verlinkt sind verweisen auch am Ende des Abschnitts auf das PEP mit der Spezifikation, Hintergründen, und Beispielen.
Ich habe zuerst bei Google geschaut, da
1. die PythonDokumentation nicht wirklich 'einfach' ist
2. man bei Google Verweise auf die genaue Problemstellung findet, und sich nicht erst duch drei Seiten Beschreibung zu einem Modul o.ä. hangeln muss, bis man das Gesuchte gefunden hat

Ich habe bspw. in der Online-Ausgabe im Quick-Search-Feld nach with gesucht; es wurde aber kein Verweis auf das with-Statement angezeigt: https://docs.python.org/3.4/search.html ... ea=default
BlackJack hat geschrieben: Wenn man Schlüsselwörter der Sprache oder etwas aus der Standardbibliothek sucht, dann ist der Index in der Python-Dokumentation sehr praktisch. Der ist auf fast allen Dokumentationsseiten oben rechts zu erreichen.
Super! Danke für den Tipp. Darüber findet man ja wirklich schnell etwas :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sfx2k hat geschrieben: 1. die PythonDokumentation nicht wirklich 'einfach' ist
Huch... ich empfinde die Doku von Python immer als eine der besten überhaupt! Imho sehr gut gegliedert, gute Beispiele und verständliche Beschreibungen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sfx2k
User
Beiträge: 54
Registriert: Dienstag 2. September 2014, 13:29

Hyperion hat geschrieben:
sfx2k hat geschrieben: 1. die PythonDokumentation nicht wirklich 'einfach' ist
Huch... ich empfinde die Doku von Python immer als eine der besten überhaupt! Imho sehr gut gegliedert, gute Beispiele und verständliche Beschreibungen :-)
Es mag gut sein, dass die Doku eine der besten ist.
Da die Delphi-Hilfe aber ganz anders aufgebaut ist, finde ich mich noch nicht so wirklich zurecht.
Wird schon... :)
Antworten