0%

Eigene Funktionen schreiben

Klare Grenzen für Funktionen wählen

Wenn du weißt, wie du eine Funktion definierst, stellt sich eine neue Frage: Welche Zeilen sollen zu einer Funktion werden?

Es gibt keine Formel, die immer die perfekte Antwort liefert. Es gibt aber hilfreiche Anhaltspunkte.

Beginne mit einer klaren Zuständigkeit

Schau noch einmal auf das Menü aus Kapitel 7. Seine Befehle benennen bereits getrennte Aufgaben:

BefehlAufgabeMögliche Funktion
addeinen Eintrag abfragen und speichernadd_item(items)
listdie gespeicherten Einträge anzeigenshow_items(items)
countanzeigen, wie viele Einträge gespeichert sindshow_count(items)

Diese Grenzen ergeben sich ganz natürlich, weil jeder Funktionsname eine vollständige Aktion beschreibt.

Vergleiche das mit:

def handle_everything():
    # print the title
    # run the menu loop
    # add items
    # list items
    # count items
    # say goodbye
    pass

Technisch ist das eine Funktion, aber ihr Name und ihr Funktionskörper verstecken weiterhin das ganze Programm an einer Stelle. Viele Aufgaben in eine Funktion zu packen macht sie nicht leichter verständlich.

Am anderen Ende des Spektrums:

def print_number(number):
    print(number)

def add_one(number):
    return number + 1

Winzige Funktionen können nützlich sein. Jeden einfachen Ausdruck auszulagern kann aber dazu führen, dass man zwischen Namen hin- und herspringen muss, ohne etwas dazuzulernen.

Eine gute erste Frage lautet:

Kann ich die Aufgabe dieser Funktion mit einer kurzen Tätigkeitsbeschreibung ausdrücken?

Show the stored items erfüllt das. Handle the menu and all its commands and also print the final report deutet darauf hin, dass mehrere Aufgaben vermischt werden.

Entscheide, was die Grenze überschreitet

Sobald die Aufgabe klar ist, stelle zwei weitere Fragen:

  1. Welche Informationen braucht die Funktion?

  2. Braucht der aufrufende Code einen Wert zurück?

Zum Beispiel:

def count_message(items):
    return f"Items stored: {len(items)}"

Die Funktion braucht die Liste, deshalb ist items ein Parameter. Der aufrufende Code braucht den fertigen Text, deshalb gibt die Funktion ihn zurück.

Diese Funktion hat eine andere Aufgabe:

def show_count(items):
    print(f"Items stored: {len(items)}")

Sie braucht ebenfalls items, aber ihre Aufgabe besteht ausdrücklich darin, die Anzahl anzuzeigen. Sie gibt sie aus und muss die Nachricht nicht zurückgeben.

Beide Entwürfe können sinnvoll sein. Entscheide danach, was der aufrufende Code braucht.

Halte den Hauptablauf lesbar

Das Ziel der Umstrukturierung in Kapitel 8 ist nicht, die Gesamtzahl der Zeilen zu verringern. Die Funktionsdefinitionen können die Datei etwas länger machen.

Die Verbesserung zeigt sich in der Menüschleife:

if command == "add":
    add_item(items)
elif command == "list":
    show_items(items)
elif command == "count":
    show_count(items)

Die Schleife liest sich jetzt wie eine Übersicht der Auswahlmöglichkeiten. Wer den Gesamtplan verstehen möchte, kann hier bleiben. Wer die Details des Auflistens wissen möchte, kann direkt zu show_items gehen.

Das ist Struktur: unterschiedliche Detailstufen, jede an einem klaren Platz.

Eine praktische Prüfliste

Bevor du eine Funktion auslagerst, frage dich:

  • Kann ich dem Vorgang einen präzisen Namen geben?

  • Hat er eine Hauptaufgabe?

  • Sind seine benötigten Parameter klar?

  • Ist klar, ob er etwas ausgeben, zurückgeben oder verändern soll?

  • Wird der aufrufende Code dadurch leichter lesbar?

Du wirst in diesen Entscheidungen besser, indem du sie triffst, das Ergebnis liest und überarbeitest.

Welche Funktion hat am deutlichsten eine einzelne Aufgabe?

Eine Funktion erstellt einen bereinigten Befehl, den eine if-Anweisung vergleichen wird. Was sollte die Funktion tun?