Overerving en polymorfisme · oefening
Overschrijf gedrag en breid het uit
Elke subklasse in de vorige les leverde een eigen is_correct. Dat heet overschrijven: een subklasse definieert een methode die de basis al heeft en de versie van de subklasse wordt uitgevoerd.
Er is nog iets wat een subklasse kan doen en het verschil doet ertoe.
Vervangen of aanvullen
describe overschrijven vervangt de basisversie volledig:
De puntenwaarde is uit de beschrijving verdwenen. De subklasse heeft de hele zin herschreven en elke verbetering die vanaf nu aan de basisversie wordt gemaakt gaat eraan voorbij.
Vaak wil je de basisversie plus iets:
super().describe() roept de basisversie aan vanuit de overschrijving en de subklasse voegt iets toe aan wat terugkomt. De punten verschijnen weer en blijven verschijnen als de basis verandert hoe die ze verwoordt.
Dit is dezelfde super() als in __init__, met dezelfde taak: voer de basisversie van deze methode uit. __init__ is alleen het geval dat je eerst tegenkomt.
De describe van een subklasse roept super().describe() aan en voegt iets toe aan het resultaat. Wat levert dat op vergeleken met het herschrijven van de hele string?
Waar elke methode vandaan komt
Bij een familie van klassen heeft ‘welke versie wordt uitgevoerd?’ een betrouwbaar antwoord nodig. Python kijkt eerst naar de eigen klasse van het object, dan naar de basis, dan naar de basis van de basis en stopt bij de eerste overeenkomst.
summary is alleen op Question gedefinieerd en roept self.describe() aan. De versie van TextQuestion wordt uitgevoerd, omdat self een TextQuestion is en het zoeken daar begint.
Het is de moeite waard hierbij stil te staan, omdat een basisklasse hierdoor meer nut heeft dan alleen code delen. Question.summary werd geschreven zonder te weten welke soorten vragen ooit zouden bestaan en roept toch de juiste describe voor elke soort aan. Een basisklasse kan de vorm van een bewerking definiëren en het variabele deel aan de subklassen overlaten.
Verantwoord overschrijven
Twee regels voorkomen dat een overschrijving de aanroepende code verrast.
Accepteer wat de basis accepteert. Als Question.is_correct(response) één argument ontvangt, moet een subklasseversie met één argument werken. Een subklasse die een extra argument vereist maakt elke aanroeper stuk die voor de basis is geschreven.
Geef terug wat de basis teruggeeft. Als is_correct True of False teruggeeft, zal een subklasse die "yes", een aantal punten of None voor ‘onbeantwoord’ teruggeeft door if question.is_correct(...) gaan en zich gedragen op manieren die niemand verwachtte.
Geen fout. Alleen Summary: None dat ergens op een scherm verschijnt. De les na de volgende gaat volledig over dit soort problemen.
Toestand én gedrag uitbreiden
__init__ volgt hetzelfde patroon en nu kun je dat zien als één geval van een algemene regel:
def __init__(self, prompt, points, tolerance):
super().__init__(prompt, points) # what every question needs
self.tolerance = tolerance # what this kind adds
Roep eerst de basis aan en voeg dan toe. Je eigen instelling doen voordat je super().__init__ aanroept is toegestaan en soms nodig, maar betekent dat een deel van het object bestaat voordat de basis klaar is. Dat is het probleem van een half opgebouwd object uit hoofdstuk 5, via een nieuwe route.
Opdracht
Elke subklasse herschrijft hier de hele beschrijving, waardoor een wijziging in de basisformulering nooit een van de subklassen zou bereiken.
Laat alle drie de basisbeschrijving uitbreiden in plaats van vervangen. Elke describe() moet super().describe() aanroepen en het eigen deel toevoegen:
TextQuestion:"<base> Type your answer."ChoiceQuestion:"<base> Options: A, B, C."met de eigen optiesNumericQuestion:"<base> Answer within 0.2."met de eigen tolerantie
De basisformulering is "<prompt> (<points> points)" en geen van de subklassen mag die opmaak bevatten. De beoordelaar verandert de basisformulering en controleert of alle drie volgen. Dat is precies waarom je het zo doet.
Voeg daarna alleen aan de basis summary() toe, dat "Summary: <description>" teruggeeft. Eén keer geschreven moet die voor elke soort de juiste beschrijving opleveren.