0%

Vom funktionierenden Code zum Programmentwurf · Übung

Halte die Daten einer Frage zusammen

Die vorige Lektion hat den Fragetext, die Antwort und die Punkte jeder Frage in einem Dictionary zusammengefasst:

question = {
    "prompt": "Which keyword starts a function?",
    "answer": "def",
    "points": 2,
}

So bleiben die zusammengehörigen Werte beieinander. Eine andere Funktion kann sie aber versehentlich wieder auseinandernehmen.

Die Frage bei jedem Aufruf auseinandernehmen

Stell dir vor, score_attempt erhält die Antwort und die Punkte als getrennte Argumente:

def score_attempt(
    answer,
    response,
    points,
):
    ...

Der Aufruf erfordert mehrere Zugriffe auf dasselbe Dictionary:

score_attempt(
    question["answer"],
    " DEF ",
    question["points"],
)

Stell dir jetzt ein Quiz mit mehreren Fragen vor. Dasselbe Muster erscheint jedes Mal:

first_score = score_attempt(
    first_question["answer"],
    first_response,
    first_question["points"],
)

second_score = score_attempt(
    second_question["answer"],
    second_response,
    second_question["points"],
)

Jeder Aufruf nimmt eine Frage auseinander und übergibt ihre Teile getrennt.

Was passiert, wenn eine Regel hinzukommt?

Angenommen, bei einigen Antworten müssen Groß- und Kleinbuchstaben jetzt genau übereinstimmen. Wir ergänzen jede Frage um einen Wert case_sensitive:

question = {
    "prompt": "Which keyword starts a function?",
    "answer": "def",
    "points": 2,
    "case_sensitive": False,
}

Diese eine neue Regel verursacht mehrere Änderungen:

  1. Füge case_sensitive zu jedem Frage-Dictionary hinzu.

  2. Füge einen weiteren Parameter zu score_attempt hinzu.

  3. Aktualisiere jede Stelle, die score_attempt aufruft.

  4. Stelle sicher, dass jeder Aufruf den neuen Wert an der richtigen Position übergibt.

Alle diese Änderungen müssen zusammenpassen. Schon eine einzige vergessene Änderung kann einen Fehler oder ein falsches Ergebnis verursachen.

Übergib die vollständige Frage

Die Antwort, die Punkte und die Regel zur Groß- und Kleinschreibung beschreiben alle dieselbe Frage. Wir können diese Frage direkt übergeben:

def score_attempt(question, response):
    ...

Die Aufrufe werden zu:

first_score = score_attempt(first_question, first_response)
second_score = score_attempt(second_question, second_response)

Wenn die Frage später eine weitere Bewertungsregel erhält, kann score_attempt sie aus dem Dictionary lesen. Die Aufrufe übergeben bereits die vollständige Frage und müssen deshalb möglicherweise nicht geändert werden.

Probiere dieselbe Idee in diesem kleineren Beispiel aus:

Try it

Füge einen weiteren Schlüssel zu question hinzu und führe das Beispiel erneut aus. Der Aufruf question_label(question) übergibt weiterhin eine vollständige Frage.

Das ist keine Regel, nach der jede Funktion ein Dictionary erhalten sollte. Getrennte Werte sind weiterhin passend, wenn sie getrennte Ideen darstellen. Hier gehören mehrere Werte bereits zu einer Frage, und die Funktion braucht mehr als einen davon.

Die Funktionen haben jetzt klar begrenzte Verantwortlichkeiten. Damit sind die Zusagen gemeint, für die jeder Programmteil zuständig ist. response_matches verantwortet die Vergleichsregel. score_attempt verantwortet die Entscheidung, die Punkte dieser Frage oder null zu vergeben. Die vollständige Frage zu übergeben lässt jede Funktion ihre eigene Aufgabe erfüllen, ohne dass jeder Aufrufer die Felder aufeinander abstimmen muss.

Warum kann score_attempt(question, response) später leichter zu ändern sein?

Die Übung lässt den Vergleichscode sichtbar und vollständig. Deine Aufgabe ist nur, score_attempt die vollständige Frage verwenden und die richtige Punktzahl zurückgeben zu lassen.

Aufgabe

Vervollständige score_attempt(question, response).

Die vorgegebene Funktion response_matches folgt bereits der Regel case_sensitive der Frage.

  • Rufe response_matches mit der vollständigen Frage und der eingegebenen Antwort auf.

  • Wenn es True zurückgibt, gib question["points"] zurück.

  • Gib andernfalls 0 zurück.

Zerlege die Frage nicht in zusätzliche Parameter.