Suche Tips zur Optimierung meines Programms

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.
Antworten
Tomes
User
Beiträge: 4
Registriert: Donnerstag 1. September 2022, 10:00

Hallo!

Ich habe mir ein kleines Programm gebastelt.
Diese Programm funktioniert zwar, kommt mir aber sehr umständlich vor.
Könnt ihr mir bitte Hinweise geben, wie ich das vereinfachen/verbessern kann.

Folgende Funktionen hat das Programm:
- Öffnen Datei "datei.txt"
- Suche nach Zeilen die mit "PETER" beginnen
- Ersetzte in dieser Zeile den Bereich [8:16] mit " TEST"
- Schreibe die Datei

Danke für die Hinweise

Code: Alles auswählen

def ersetzten():
    s = []
    mater_liste = []
    global line; s
    line = []
    s = []
    with open ("datei.txt", "r", encoding='cp1252') as file:
        for line in file:
            y = line.startswith("PETER")
            if y == True:
                line = line[:8] + "    TEST" + line[16:]
            s.append(line)
    with open ("datei_neu.txt", "w", encoding='cp1252') as file:
        file.write("".join((map(str, s))))
ersetzten()
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tomes: In `ersetzten()` ist ein "t" zu viel und die Funktion sollte nur aufgerufen werden wenn das als Programm ausgeführt wird, aber nicht wenn es als Modul in einem anderen Modul oder in einer Python-Shell importiert wird.

``global`` hat in einem sauberen Programm nichts zu suchen. Vergiss das Schlüsselwort am besten sofort wieder. Es macht hier auch überhaupt gar keinen Sinn. Zudem vermute ich Du dachtest `s` wäre auch ``global``‽ Ist es nicht, weil das Semikolon Anweisungen trennt.

Code: Alles auswählen

global line; s

# ist das gleiche wie

global line
s
Und `s` einfach so als Anweisung hat keinen Effekt. Da wird der Wert von `s` ermittelt, und dann wird damit einfach nichts gemacht und mit der nächsten Anweisung im Programm fortgefahren.

`s` wird zweimal an eine jeweils neue leere Liste gebunden. Einmal reicht.

`mater_liste` wird definiert aber nirgends verwendet.

`line` wird vor der Schleife ebenfalls an eine leere Liste gebunden, aber auch die wird nirgends verwendet, und zusätzlich werden *in* der Schleife dann Zeichenketten an den Namen `line` gebunden. Man sollte im gleichen Namensraum nur Objekte vom gleichen (Duck-)Typ an einen Namen binden. Sonst ist irgendwann der Leser verwirrt und versteht nicht mehr was da passiert.

Namen sollte keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. Einbuchstabige Namen sind selten gute Namen. `i` und `j` für einen Laufindex oder `x`, `y`, und `z` für Koordinaten geht, `s` für eine Liste mit Zeilen oder `y` für das Ergebnis eines Tests sind unverständlich.

`y` braucht man auch überhaupt nicht. Das wird ja nur einmal in der nächsten Zeile als ``if``-Bedingung verwendet. Da kann man auch gleich das `startswith()` einsetzen.

Man macht keine Vergleiche mit literalen Wahrheitswerten. Bei dem Vergleich kommt doch nur wieder ein Wahrheitswert bei heraus. Entweder der, den man sowieso schon hatte; dann kann man den auch gleich nehmen. Oder das Gegenteil davon; dafür gibt es ``not``.

Code: Alles auswählen

            y = line.startswith("PETER")
            if y == True:
                ...
            
            # =>
            
            if line.startswith("PETER"):
                ...
Dann wären wir für das einlesen und verändern der Daten bei diesem Code:

Code: Alles auswählen

        lines = []
        for line in file:
            if line.startswith("PETER"):
                line = f"{line[:8]}    TEST{line[16:]}"
            lines.append(line)
Oder als „list comprehension“:

Code: Alles auswählen

        lines = [
            (
                f"{line[:8]}    TEST{line[16:]}"
                if line.starswith("PETER")
                else line
            )
            for line in file
        ]
`s` beziehungsweise `lines` enthält bereits Zeichenketten. Da mit `map()` noch mal `str()` auf jede Zeichenkette anzuwenden macht keinen Sinn.

Ja, man kann die ganzen Zeilen per `join()` vor dem schreiben zu einer grossen Zeichenkette zusammensetzen. Man könnte aber auch einfach die `writelines()`-Methode mit den Zeilen aufrufen. Womit das schreiben der Datei dann so aussieht:

Code: Alles auswählen

    with open("datei_neu.txt", "w", encoding="cp1252") as file:
        file.writelines(lines)
Jetzt könnte man sich auch noch überlegen ob überhaupt mehr als eine Zeile von der Eingangsdatei zur gleichen Zeit im Speicher sein muss. Deswegen macht die „list comprehension“ beim Lesen Sinn, auch wenn die das gleiche wie die Schleife macht: Man kann da trivial einen Generatorausdruck machen, den man direkt an `writelines()` übergeben kann, womit wir dann bei folgendem Programm wären:

Code: Alles auswählen

#!/usr/bin/env python3

ENCODING = "cp1252"


def ersetzen():
    with open("datei.txt", "r", encoding=ENCODING) as lines:
        with open("datei_neu.txt", "w", encoding=ENCODING) as file:
            file.writelines(
                (
                    f"{line[:8]}    TEST{line[16:]}"
                    if line.starswith("PETER")
                    else line
                )
                for line in lines
            )


if __name__ == "__main__":
    ersetzen()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Tomes
User
Beiträge: 4
Registriert: Donnerstag 1. September 2022, 10:00

Danke für die ausführliche Hilfe.

Diese Schleife kann ich nicht lesen/verstehen:

Code: Alles auswählen

        lines = [
            (
                f"{line[:8]}    TEST{line[16:]}"
                if line.starswith("PETER")
                else line
            )
            for line in file
        ]
 
Daher habe ich für mich den Code abgeändert:

Code: Alles auswählen

#!/usr/bin/env python3

ENCODING = "cp1252"


def ersetzen():
    with open("datei.txt", "r", encoding=ENCODING) as lines:
        with open("datei_neu.txt", "w", encoding=ENCODING) as file:
            for line in lines:
                if line.startswith("PETER"):
                    file.writelines(f"{line[:8]}    TEST{line[16:]}")
                else: 
                    file.writelines(line)

if __name__ == "__main__":
    ersetzen()
Ich glaube diese Variante kann man auch vorzeigen.
Danke für die Unterstützung. Ich konnte viel lernen.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tomes: Da wird `writelines()` falsch verwendet. Das funktioniert nur weil Zeichenketten iterierbar sind und dabei wieder Zeichenketten der Länge 1 liefern, mit den einzelnen Zeichen, die `writelines()` dann einzeln schreibt, anstatt die ganze Zeile auf einmal. Das solltest Du in `write()` ändern:

Code: Alles auswählen

                if line.startswith("PETER"):
                    file.write(f"{line[:8]}    TEST{line[16:]}")
                else: 
                    file.write(line)
Wobei ich an der Stelle dann doch eher wieder zu dem zurück gehen würde was Du am Anfang gemacht hast: `line` einen neuen Wert zu weisen, falls `line` mit "PETER" anfängt, und nur einmal einen `write()`-Aufruf im Code stehen zu haben. Oder aber nur einen `write()`-Aufruf durch einen bedingten Ausdruck, wie in der „list comprehension“ beziehungsweise dem Generatorausdruck:

Code: Alles auswählen

            for line in lines:
                file.write(
                    f"{line[:8]}    TEST{line[16:]}"
                    if line.startswith("PETER")
                    else line
                )
Wobei man dann ja wieder nur noch einen Schritt vom Generatorausdruck und `writelines()` entfernt ist.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
Dennis89
User
Beiträge: 1562
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,
Tomes hat geschrieben: Freitag 2. September 2022, 06:16
Diese Schleife kann ich nicht lesen/verstehen:

Code: Alles auswählen

        lines = [
            (
                f"{line[:8]}    TEST{line[16:]}"
                if line.starswith("PETER")
                else line
            )
            for line in file
        ]
 
Vielleicht hilft dir die Erklärung aus der Doku weiter.
Und falls dich die geschwungenen Klammern und das 'f' noch verwirren, dann kannst du dir diesen Teil noch durchlesen.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Tomes
User
Beiträge: 4
Registriert: Donnerstag 1. September 2022, 10:00

Danke für die weiteren Hinweise.

Die Position der Schleife, damit tue ich mir schwer.
Dazu bin ich einfach noch zu ungeübt.

Zuerst die Aufforderung "file.write" dann erst die "if" Abfrage. Für mein Verständnis muss das umgekehrt sein.

Übersetzt für mein Verständnis: Schreibe die geänderte Zeile, aber nur wenn PETER am Anfang der Zeile steht.
Das ist doch die richtige Bedeutung der Schleife oder?

Code: Alles auswählen

            for line in lines:
                file.write(
                    f"{line[:8]}    TEST{line[16:]}"
                    if line.startswith("PETER")
                    else line
                )
Ich muss damit arbeiten, um das besser zu verstehen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, das heisst das nicht. Das ist der ternaere Operator in Python:

Code: Alles auswählen

"werd-wenn-bedingung-wahr" if bedingung else "wert-wenn-bedingung-falsch"
Das file.write wird also *immer* ausgefuehrt, nur wahlweise mit der umgeschriebenen Zeile, oder einfach der Zeile, wie sie vorher war.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tomes: Erst `file.write()` und dann das was geschrieben werden soll: Die veränderte Zeile wenn die Zeile mit "PETER" beginnt, sonst die Zeile unverändert. Das Argument ist ein bedingter Ausdruck der je nach Bedingung das eine oder das andere Ergebnis liefert. Die Syntax ist ``<a> if <bedingung> else <b>``. Im Beispiel auf drei Zeilen umgebrochen damit die eine Zeile nicht zu lang wird. Und man kann das wie jeden Ausdruck an Stelle eines Wertes einsetzen, also beispielsweise als Argument bei einem Funktions- oder Methodenaufruf.

Als Beispiel hier mal ein Ausdruck der je nach dem ob `n` gerade oder ungerade ist, die Zeichenkette "gerade" oder "ungerade" ergibt, und dann als Argument an `print()` übergeben:

Code: Alles auswählen

In [45]: n = 42

In [46]: "gerade" if n % 2 == 0 else "ungerade"
Out[46]: 'gerade'

In [47]: n = 23

In [48]: "gerade" if n % 2 == 0 else "ungerade"
Out[48]: 'ungerade'

In [49]: print(n, "ist", "gerade" if n % 2 == 0 else "ungerade")
23 ist ungerade
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Tomes
User
Beiträge: 4
Registriert: Donnerstag 1. September 2022, 10:00

Ach so, nun fange ich an das zu verstehen!

Danke.
Antworten