Opstap¶
Overerving¶
Leerdoel: een subklasse schrijven die met super() voortbouwt op haar
superklasse, een methode overschrijven, een parameter een standaardwaarde geven,
voorspellen welke versie van een methode er draait bij een lijst van
verschillende objecten, en een klasse schrijven die door duck typing meedoet
zonder subklasse te zijn.
Deze opdrachten zijn kaal met opzet. Ze oefenen de handgrepen die je bij de basisopgave en het practicum van deze week nodig hebt, zodat je daar met het probleem bezig kunt zijn in plaats van met de syntaxis.
De opdrachten bouwen voort op de klasse Lamp uit de opstap van week 5, met één
methode erbij: describe, die een korte beschrijving teruggeeft. Voer de cel
hieronder eerst uit.
class Lamp:
"""Een lamp die aan of uit staat."""
def __init__(self, color):
"""Maak een lamp met de gegeven kleur; hij staat uit."""
self.color = color
self.on = False
def switch(self):
"""Zet de lamp aan als hij uit staat, en uit als hij aan staat."""
self.on = not self.on
def describe(self):
"""Geeft een korte beschrijving, zoals lamp rood: uit."""
if self.on:
return f"lamp {self.color}: aan"
return f"lamp {self.color}: uit"
Opdracht 1¶
Voorspel eerst wat deze code afdrukt, en voer haar daarna uit:
class NightLamp(Lamp):
"""Een nachtlampje."""
n = NightLamp("geel")
n.switch()
print(n.on)
print(n.describe())
class NightLamp(Lamp): maakt NightLamp een subklasse van Lamp, en
Lamp de superklasse van NightLamp. De klasse bevat alleen een docstring.
# voer de code hier uit
Opdracht 2¶
Beantwoord als commentaar in de cel hieronder: in NightLamp staat geen enkele
methode. Waar komen __init__, switch en describe van een nachtlampje dan
vandaan?
# jouw antwoord
Opdracht 3¶
Schrijf een subklasse DimmableLamp van Lamp: een lamp die je kunt dimmen. Haar
constructor krijgt twee argumenten, color en level. Hij laat de constructor van
Lamp het werk voor color doen, en slaat level op in het attribuut
self.level.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
|
|
Hint
super().__init__(color) roept de constructor van de superklasse aan. Die geeft
self.color en self.on hun beginwaarde. Daarna zet je zelf self.level.
# jouw oplossing
d = DimmableLamp("wit", 30)
assert d.color == "wit"
assert not d.on
assert d.level == 30
d.switch()
assert d.on
Opdracht 4¶
Een dimbare lamp brandt meestal op volle sterkte. Geef de parameter level een
standaardwaarde: schrijf level=100 in plaats van level. Laat je het
argument dan weg, dan krijgt level de waarde 100. Voer daarna de cel van
opdracht 3 opnieuw uit, en dan de test hieronder.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
assert DimmableLamp("wit").level == 100
assert DimmableLamp("wit", 30).level == 30
assert DimmableLamp("wit", 0).level == 0
Opdracht 5¶
Geef DimmableLamp een eigen methode describe. Die geeft wat describe van
Lamp teruggeeft, met de helderheid erachter. DimmableLamp overschrijft zo
de methode describe van Lamp. Voer daarna de cel van opdracht 3 opnieuw uit,
en dan de test hieronder.
Code |
|
|---|---|
|
|
daarna |
|
Hint
Schrijf de tekst van Lamp niet opnieuw. super().describe() roept de methode
describe van de superklasse aan en geeft haar tekst terug; daar plak je de
helderheid achter.
d = DimmableLamp("wit", 30)
assert d.describe() == "lamp wit: uit, 30%"
d.switch()
assert d.describe() == "lamp wit: aan, 30%"
assert DimmableLamp("blauw").describe() == "lamp blauw: uit, 100%"
Opdracht 6¶
Deze subklasse heeft een constructor die super().__init__ niet aanroept.
Voorspel eerst wat de code afdrukt, en voer haar daarna uit:
class BrokenLamp(Lamp):
"""Een lamp met een constructor die iets vergeet."""
def __init__(self, color, level):
"""Maak een lamp met de gegeven kleur en helderheid."""
self.level = level
b = BrokenLamp("rood", 50)
print(b.level)
print(b.describe())
# voer de code hier uit
Beantwoord als commentaar in de cel hieronder: welke attributen ontbreken er, en waarom? De foutmelding noemt er maar één. En wat moet er in de constructor bij?
# jouw antwoord
Opdracht 7¶
Schrijf een subklasse CountingLamp van Lamp: een lamp die telt hoe vaak hij is
omgezet.
Methode |
Doet |
|---|---|
|
doet wat de constructor van |
|
zet de lamp om zoals |
Hint
In switch roep je eerst super().switch() aan. Zo schrijf je het omzetten zelf
niet opnieuw.
# jouw oplossing
c = CountingLamp("blauw")
assert c.count == 0
assert not c.on
c.switch()
c.switch()
c.switch()
assert c.count == 3
assert c.on
assert c.describe() == "lamp blauw: aan"
Opdracht 8¶
Een leeslamp is een dimbare lamp die op 60% staat. Voorspel eerst wat deze code afdrukt, en voer haar daarna uit:
class ReadingLamp(DimmableLamp):
"""Een leeslamp: een dimbare lamp die op 60% staat."""
def __init__(self, color):
"""Maak een leeslamp op 60%."""
super().__init__(color, 60)
r = ReadingLamp("groen")
r.switch()
print(r.describe())
Beantwoord daarna als commentaar in de cel: welke describe draait er bij r, die
van Lamp of die van DimmableLamp? En welke van de twee zet lamp groen: aan
in de tekst?
# voer de code hier uit
Opdracht 9¶
Hieronder staat een lijst met vier verschillende lampen. Voorspel eerst wat de lus afdrukt, en voer haar daarna uit:
lamps = [Lamp("rood"), DimmableLamp("wit", 30), NightLamp("geel"), ReadingLamp("groen")]
for lamp in lamps:
lamp.switch()
print(lamp.describe())
In de lus staat één aanroep, lamp.describe(). Welke versie van describe er
draait, hangt af van het object. Dat heet polymorfisme.
# voer de code hier uit
Opdracht 10¶
Een kaars is geen lamp: hij heeft geen kleur en brandt of brandt niet. Schrijf een
klasse Candle die geen subklasse van Lamp is, maar wel dezelfde twee
methoden heeft als een lamp, zodat hij mee kan doen in een lus als die van
opdracht 9.
Methode |
Doet |
|---|---|
|
maakt een kaars die niet brandt: |
|
steekt de kaars aan als hij uit is, en blaast hem uit als hij brandt |
|
geeft |
# jouw oplossing
k = Candle()
assert k.describe() == "kaars: uit"
k.switch()
assert k.describe() == "kaars: brandt"
things = [Lamp("rood"), Candle(), DimmableLamp("wit", 30)]
for thing in things:
thing.switch()
assert [thing.describe() for thing in things] == [
"lamp rood: aan",
"kaars: brandt",
"lamp wit: aan, 30%",
]
Opdracht 11¶
Beantwoord als commentaar in de cel hieronder:
Candleerft niets vanLamp. Waarom werkt de lus in de test van opdracht 10 dan toch?Wat gebeurt er als je een object in de lijst zet dat geen
describeheeft? Op welk moment merk je dat?
Dat een object alleen de methoden en attributen hoeft te hebben die worden gebruikt, en niet van een bepaalde klasse hoeft te zijn, heet duck typing: als het loopt als een eend en kwaakt als een eend, dan is het een eend.
# jouw antwoord