mitmario.dev

Ein Programm aufteilen

Python Sandbox 4 Min Lesezeit 5 BeispieleLektion 8 von 9

Programme wachsen. Erst sind es fünf Zeilen, dann zwanzig, dann fünfzig, und irgendwann fährst du beim Lesen mit dem Finger mit, um nicht zu vergessen, wo du gerade bist. Es gibt ein recht zuverlässiges Zeichen dafür, dass ein Programm in Teile zerlegt werden will. Man fängt an, Kommentare zu schreiben, die sagen, wo man gerade ist.

Ein Skript am Stück

Ein Skript am Stück
# Kaffeekasse: Wer hat wie viel eingezahlt?
einzahlungen = [("Mia", 5.00), ("Ole", 2.50), ("Mia", 3.00), ("Aylin", 10.00), ("Ole", 2.50)]

# jetzt wird zusammengezählt
summen = {}
for name, betrag in einzahlungen:
    summen[name] = summen.get(name, 0) + betrag

# und jetzt kommt die Ausgabe
print("Kaffeekasse")
for name in sorted(summen):
    print(f"{name:<10}{summen[name]:6.2f}")
print(f"{'Zusammen':<10}{sum(summen.values()):6.2f}")

Eine Kaffeekasse im Büro, und du willst wissen, wer diese Woche wie viel eingezahlt hat. Das Programm ist kurz und tut, was es soll. Die beiden Kommentare # jetzt wird zusammengezählt und # und jetzt kommt die Ausgabe sind trotzdem ein Hinweis.

Sie trennen Abschnitte, die nichts miteinander zu tun haben, außer dass der eine dem anderen seine Ergebnisse weiterreicht. Dafür gibt es Funktionen. Ein Name erklärt einen Abschnitt besser als ein Kommentar darüber.

In Funktionen zerlegt

In Funktionen zerlegt
def zusammenzaehlen(einzahlungen):
    summen = {}
    for name, betrag in einzahlungen:
        summen[name] = summen.get(name, 0) + betrag
    return summen


def bericht(summen):
    print("Kaffeekasse")
    for name in sorted(summen):
        print(f"{name:<10}{summen[name]:6.2f}")
    print(f"{'Zusammen':<10}{sum(summen.values()):6.2f}")


def main():
    einzahlungen = [("Mia", 5.00), ("Ole", 2.50), ("Mia", 3.00), ("Aylin", 10.00), ("Ole", 2.50)]
    summen = zusammenzaehlen(einzahlungen)
    bericht(summen)


main()

Dasselbe Programm, dieselbe Ausgabe, aber in drei Teilen. zusammenzaehlen rechnet und gibt das Ergebnis zurück, wie es sich für Funktionen gehört, die etwas ausrechnen. bericht gibt aus, das ist ihre ganze Aufgabe. Und main ruft beide in der richtigen Reihenfolge auf und reicht weiter, was die eine liefert und die andere braucht.

main heißt so, weil es die Hauptfunktion ist (main, Haupt-). Der Name ist eine Abmachung und keine Regel von Python. Wenn du ein fremdes Programm öffnest, suchst du zuerst nach ihr, denn sie liest sich wie ein Inhaltsverzeichnis. Drei Zeilen, und du weißt, was das Programm tut, ohne eine einzige Schleife gelesen zu haben. Wie es das im Einzelnen tut, steht in den Funktionen darüber, und die liest man erst, wenn man es wissen will.

Ganz unten steht dann nur noch ein einziger Aufruf, main(). Er muss unter allen def stehen, denn erst wenn alle Funktionen angelegt sind, darf die erste laufen. In welcher Reihenfolge die Funktionen darüber stehen, ist dagegen egal.

Wie fein man zerlegt, ist eine Frage des Augenmaßes. Ein guter Anhaltspunkt ist ein einziges Wort. Wenn du für das, was eine Funktion tut, ein „und“ brauchst, hast du zwei vor dir. „Rechnet die Summen aus“ ist eine Funktion. „Rechnet die Summen aus und druckt sie“ sind zwei, und so ist das Beispiel geschnitten.

Wenn eine andere Datei es benutzt

Importiert, und schon läuft es
def zusammenzaehlen(einzahlungen):
    summen = {}
    for name, betrag in einzahlungen:
        summen[name] = summen.get(name, 0) + betrag
    return summen


def bericht(summen):
    print("Kaffeekasse")
    for name in sorted(summen):
        print(f"{name:<10}{summen[name]:6.2f}")
    print(f"{'Zusammen':<10}{sum(summen.values()):6.2f}")


def main():
    einzahlungen = [("Mia", 5.00), ("Ole", 2.50), ("Mia", 3.00), ("Aylin", 10.00), ("Ole", 2.50)]
    summen = zusammenzaehlen(einzahlungen)
    bericht(summen)


main()

Zerlegte Programme haben noch einen Vorteil. Ihre Teile lassen sich woanders benutzen. Hier holt sich auswertung.py die Funktion zusammenzaehlen mit import aus kasse.py, um eine eigene kleine Rechnung zu machen. Ausgegeben werden soll nur die eine Zeile über Mia.

Vorher kommt aber der ganze Bericht der Kaffeekasse. Beim Importieren führt Python die Datei kasse.py einmal von oben bis unten aus, damit die Funktionen darin angelegt werden. Und ganz unten steht main(). Also läuft das ganze Programm mit.

Die pruefen.py der Aufgaben importiert deine Funktionen seit Lektion 8.2 genauso. Stünde in main.py ein Programm, liefe es dort jedes Mal mit.

Wie heißt diese Datei?

Wie heißt diese Datei?
import helfer

print("main.py heißt hier:", __name__)

Die Lösung hängt an einem Namen, den Python jeder Datei gibt, die läuft, nämlich __name__ mit zwei Unterstrichen davor und dahinter. Bei einer Datei, die importiert wird, ist das ihr eigener Name ohne .py, hier helfer. Bei der Datei, die gestartet wurde, ist es immer __main__, egal wie sie heißt. Das stand schon in der ersten Zeile von help().

Nur wenn sie gestartet wird

Nur wenn sie gestartet wird
def zusammenzaehlen(einzahlungen):
    summen = {}
    for name, betrag in einzahlungen:
        summen[name] = summen.get(name, 0) + betrag
    return summen


def bericht(summen):
    print("Kaffeekasse")
    for name in sorted(summen):
        print(f"{name:<10}{summen[name]:6.2f}")
    print(f"{'Zusammen':<10}{sum(summen.values()):6.2f}")


def main():
    einzahlungen = [("Mia", 5.00), ("Ole", 2.50), ("Mia", 3.00), ("Aylin", 10.00), ("Ole", 2.50)]
    summen = zusammenzaehlen(einzahlungen)
    bericht(summen)


if __name__ == "__main__":
    main()

Damit lässt sich fragen, ob diese Datei die ist, die gestartet wurde. if __name__ == "__main__": ist diese Frage, und darunter steht der Aufruf main(). Wird kasse.py gestartet, stimmt die Bedingung, und der Bericht kommt. Importiert eine andere Datei sie, heißt sie kasse. Dann stimmt die Bedingung nicht, und es werden nur die Funktionen angelegt. auswertung.py gibt jetzt nur noch ihre eine Zeile aus.

Beim ersten Mal sieht die Zeile aus wie eine Beschwörungsformel, und man kann sie jahrelang abschreiben, ohne sie je zu lesen. Gelesen heißt sie, dass etwas nur läuft, wenn diese Datei gestartet wird, und nicht, wenn eine andere sie benutzt. Mehr dazu und zu import überhaupt steht in Lektion 10.3. Für jetzt reicht, dass jedes Programm ab hier so endet.

Was die Aufgabe will

Ein Wochenbericht für das Haushaltsbuch, gut dreißig Zeilen am Stück, mit Kommentaren, die sagen, wo man gerade ist. Er soll in Funktionen zerfallen, ohne dass sich an der Ausgabe etwas ändert. Zuletzt kommt der Aufruf von main() hinter if __name__ == "__main__":.

Zum Mitnehmen

Ein längeres Skript zerlegt man in Funktionen, von denen jede eine Aufgabe hat, meistens einlesen, rechnen und ausgeben. Dazu kommt eine Funktion main(), die sie der Reihe nach aufruft. main() selbst wird unter if __name__ == "__main__": aufgerufen, damit das Programm nur läuft, wenn die Datei gestartet wird, und nicht, wenn eine andere sie importiert.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 5 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.