0%

Iteration als Schnittstelle · Abschlussprojekt

Abschlussprojekt: Projekt: Einen iterierbaren Fragenbestand bauen

Frühere Projekte gaben Momentaufnahmen als Listen über benannte Methoden heraus. Dieses Projekt kombiniert das Iterationsprotokoll der letzten Lektionen mit verzögertem Filtern und begrenztem Verbrauch.

Was du baust

Einen QuestionBank, der Fragen hält und drei Arten von Anfragen beantwortet:

for question in bank              every graded question, in order
bank.by_topic("loops")            a lazy view, filtered
bank.sample(3)                    at most three, from any source

Das erste ist __iter__. Das zweite und dritte sind Generatoren. Keines baut eine Liste auf, sofern der aufrufende Code keine Liste verlangt.

Die Entscheidungen

__iter__ überspringt unbewertete Fragen. Ein Bestand enthält auch Umfrageelemente. Ihn zu durchlaufen bedeutet, die zählenden Fragen zu durchlaufen. Das ist eine echte Entscheidung. Deshalb ist __iter__ hier ein Generator statt der einzeiligen Delegation aus Lektion 4.

by_topic gibt einen Generator zurück und sagt das. Aufrufender Code möchte vielleicht nur den ersten Treffer. Diese Form erlaubt es, dann aufzuhören.

sample hat eine exakte Grenze. Die Anfrage nach zwei Fragen liefert und verbraucht zwei, nicht drei. Bei null oder einer negativen Anzahl beginnt kein Durchlauf des Bestands.

summary gibt eine Liste zurück. Sie ist kurz, und aufrufender Code wird sie indizieren und messen. Ein Generator wäre ohne Nutzen umständlicher. Das ist die angewandte Regel aus Lektion 6.

Warum ist __iter__ hier als Generator statt mit return iter(self._questions) geschrieben?

Regeln, die die Bewertung prüft

  • Der Bestand ist wiederholt durchlaufbar: Zwei Durchläufe sehen zweimal alles.

  • by_topic und sample sind Generatorfunktionen, keine Funktionen mit Listen als Rückgabewert.

  • sample funktioniert mit einer endlosen Quelle und muss deshalb selbst aufhören.

  • sample verbraucht keine Frage über die angeforderte Anzahl hinaus.

  • first_unanswered untersucht nicht mehr Fragen als nötig.

  • points_needed gibt 0 zurück, wenn das Ziel schon zu Beginn erreicht ist.

  • summary gibt eine Liste zurück.

Fertigstellen

Lies deinen QuestionBank und frage für jede Methode, was aufrufender Code mit ihrem Ergebnis tun kann. Eine liefert etwas Messbares und Indizierbares. Die anderen liefern etwas, das einmal durchlaufen und früh beendet werden kann. Beides sind richtige Antworten auf unterschiedliche Fragen.

Aufgabe

Baue QuestionBank und die beiden verwendenden Funktionen.

add(question) speichert eine Frage. __iter__ ist ein Generator, der jede Frage mit mehr als null Punkten in ihrer Reihenfolge liefert und unbewertete Fragen überspringt.

by_topic(topic) ist ein Generator für bewertete Fragen mit passendem Thema. sample(count) ist ein Generator für höchstens count bewertete Fragen. Er fordert vom Iterator des Bestands nicht mehr bewertete Fragen an, als er liefert. Beim Finden dürfen weiterhin gespeicherte unbewertete Fragen untersucht werden. Bei null oder einer negativen Anzahl wird nichts verbraucht.

total_points ist eine schreibgeschützte Property, die die bewerteten Fragen mit einem Generatorausdruck statt einer Liste summiert.

summary() gibt eine Liste von Strings der Form "<prompt> (<points>)" zurück, weil aufrufender Code ihre Länge abfragt und sie indiziert.

Dann folgen zwei Funktionen, die jedes Iterable von Fragen entgegennehmen, nicht nur einen Fragenbestand:

  • first_unanswered(questions, answered) gibt die erste Frage zurück, deren Fragetext nicht in answered enthalten ist, oder None. Es muss sofort beim ersten Treffer aufhören.

  • points_needed(questions, target) gibt zurück, wie viele Fragen in ihrer Reihenfolge benötigt werden, bis ihre Punkte target erreichen. Wird das Ziel nie erreicht, lautet das Ergebnis None. Gib bei target <= 0 den Wert 0 zurück, denn dieses Ziel ist schon vor dem Verbrauch einer Frage erreicht.