Opstap¶
De uitwerkingen staan in dezelfde volgorde als de elf opdrachten in problems/13_opstap.
De klasse Lamp waar alle opdrachten op voortbouwen:
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¶
NightLamp erft alles van Lamp. De uitvoer is True en daarna
lamp geel: aan.
class NightLamp(Lamp):
"""Een nachtlampje."""
n = NightLamp("geel")
n.switch()
print(n.on)
print(n.describe())
True
lamp geel: aan
Opdracht 2¶
Uit de superklasse Lamp. Een subklasse krijgt alle methoden van haar
superklasse mee. NightLamp("geel") roept dus de constructor van Lamp aan, en
n.switch() en n.describe() draaien de versies uit Lamp.
Opdracht 3¶
De klasse hieronder bevat ook de standaardwaarde van opdracht 4 en de methode
describe van opdracht 5. super().__init__(color) geeft color en on hun
beginwaarde; daarna zet de constructor level.
class DimmableLamp(Lamp):
"""Een lamp die je kunt dimmen."""
def __init__(self, color, level=100):
"""Maak een dimbare lamp; zonder level brandt hij op 100%."""
super().__init__(color)
self.level = level
def describe(self):
"""Geeft de beschrijving van een lamp, met de helderheid erachter."""
return super().describe() + f", {self.level}%"
d = DimmableLamp("wit", 30)
assert d.color == "wit"
assert not d.on
assert d.level == 30
d.switch()
assert d.on
Opdracht 4¶
Met level=100 in de signatuur mag je het argument weglaten. Geef je het wel mee,
dan geldt wat je meegeeft, ook als dat 0 is.
assert DimmableLamp("wit").level == 100
assert DimmableLamp("wit", 30).level == 30
assert DimmableLamp("wit", 0).level == 0
Opdracht 5¶
super().describe() geeft de tekst van Lamp, zoals lamp wit: uit. De
methode van DimmableLamp plakt daar alleen 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¶
De uitvoer is 50, en daarna een foutmelding:
AttributeError: 'BrokenLamp' object has no attribute 'on'.
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())
50
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[7], line 11
7
8
9 b = BrokenLamp("rood", 50)
10 print(b.level)
---> 11 print(b.describe())
Cell In[1], line 15, in Lamp.describe(self)
13 def describe(self):
14 """Geeft een korte beschrijving, zoals lamp rood: uit."""
---> 15 if self.on:
16 return f"lamp {self.color}: aan"
17 return f"lamp {self.color}: uit"
AttributeError: 'BrokenLamp' object has no attribute 'on'
De attributen on en color ontbreken; de foutmelding noemt alleen on, omdat
describe daar het eerst op vastloopt. Die twee krijgen hun beginwaarde in
de constructor van Lamp, en die wordt nu niet aangeroepen: de constructor van
BrokenLamp overschrijft hem. describe van Lamp leest self.on en loopt
daarop vast. In de constructor hoort als eerste regel
super().__init__(color).
Opdracht 7¶
switch roept eerst de versie van Lamp aan, en telt daarna.
class CountingLamp(Lamp):
"""Een lamp die telt hoe vaak hij is omgezet."""
def __init__(self, color):
"""Maak een lamp die nog nooit is omgezet."""
super().__init__(color)
self.count = 0
def switch(self):
"""Zet de lamp om, en telt dat."""
super().switch()
self.count += 1
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¶
De uitvoer is lamp groen: aan, 60%. ReadingLamp heeft zelf geen describe,
dus draait die van haar superklasse DimmableLamp. Die roept op haar beurt met
super().describe() de versie van Lamp aan, en die zet lamp groen: aan in de
tekst.
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())
lamp groen: aan, 60%
Opdracht 9¶
Elke lamp gebruikt haar eigen describe, of die van de dichtstbijzijnde
superklasse die er een heeft:
lamps = [Lamp("rood"), DimmableLamp("wit", 30), NightLamp("geel"), ReadingLamp("groen")]
for lamp in lamps:
lamp.switch()
print(lamp.describe())
lamp rood: aan
lamp wit: aan, 30%
lamp geel: aan
lamp groen: aan, 60%
Opdracht 10¶
Candle heeft geen superklasse tussen haakjes.
class Candle:
"""Een kaars: geen lamp, maar je kunt hem wel aan- en uitdoen."""
def __init__(self):
"""Maak een kaars die nog niet brandt."""
self.lit = False
def switch(self):
"""Steekt de kaars aan als hij uit is, en blaast hem uit als hij brandt."""
self.lit = not self.lit
def describe(self):
"""Geeft een korte beschrijving, zoals kaars: brandt."""
if self.lit:
return "kaars: brandt"
return "kaars: uit"
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¶
De lus roept bij elk object alleen
switchendescribeaan. Welke klasse het object heeft, maakt daarvoor niet uit, zolang het die twee methoden heeft. Een kaars heeft ze.Dan geeft de aanroep
describe()eenAttributeError, en wel pas op het moment dat de lus bij dat object komt. Het object in de lijst zetten gaat nog goed.