Opstap¶
De uitwerkingen staan in dezelfde volgorde als de tien opdrachten in problems/14_opstap.
De klassen Lamp en DimmableLamp zoals je ze begint, uit de opstap van week 6:
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 __repr__(self):
"""Geeft de lamp als string, met de naam van de klasse en de kleur."""
return f"{self.__class__.__name__}({self.color})"
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"
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}%"
Opdracht 1¶
Twee keer False. a en b zijn twee verschillende objecten, en bij je eigen
klassen vergelijkt == of het hetzelfde object is: de identiteit, net als is.
a = Lamp("rood")
b = Lamp("rood")
print(a == b)
print(a is b)
False
False
Opdracht 2¶
__eq__ controleert eerst of other een lamp is. Is het iets anders, dan is
het antwoord False. != hoef je niet zelf te schrijven: Python keert het
antwoord van __eq__ om.
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 __repr__(self):
"""Geeft de lamp als string, met de naam van de klasse en de kleur."""
return f"{self.__class__.__name__}({self.color})"
def __eq__(self, other):
"""Geeft True als other een lamp is met dezelfde kleur en stand."""
if not isinstance(other, Lamp):
return False
return self.color == other.color and self.on == other.on
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"
a = Lamp("rood")
b = Lamp("rood")
assert a == b
assert a is not b
assert not a == Lamp("blauw")
a.switch()
assert not a == b
assert a != b
assert not Lamp("rood") == "rood"
Opdracht 3¶
sorted gebruikt <, en dus __lt__. Is other geen dimbare lamp, dan gooit
__lt__ een TypeError; dat laat opdracht 10 zien.
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 __lt__(self, other):
"""Geeft True als deze lamp minder fel is dan other."""
if not isinstance(other, DimmableLamp):
raise TypeError("een DimmableLamp is alleen met een DimmableLamp te vergelijken")
return self.level < other.level
def describe(self):
"""Geeft de beschrijving van een lamp, met de helderheid erachter."""
return super().describe() + f", {self.level}%"
dim = DimmableLamp("wit", 30)
bright = DimmableLamp("wit", 80)
assert dim < bright
assert not bright < dim
assert not dim < DimmableLamp("geel", 30)
lamps = [DimmableLamp("rood", 60), DimmableLamp("wit", 10), DimmableLamp("geel", 100)]
assert [lamp.level for lamp in sorted(lamps)] == [10, 60, 100]
Opdracht 4¶
De controle staat vooraan in de constructor, voordat er een attribuut een waarde krijgt.
class DimmableLamp(Lamp):
"""Een lamp die je kunt dimmen."""
def __init__(self, color, level=100):
"""Maak een dimbare lamp; gooit een ValueError bij een onmogelijke level."""
if level < 0 or level > 100:
raise ValueError("de helderheid moet van 0 tot en met 100 zijn")
super().__init__(color)
self.level = level
def __lt__(self, other):
"""Geeft True als deze lamp minder fel is dan other."""
if not isinstance(other, DimmableLamp):
raise TypeError("een DimmableLamp is alleen met een DimmableLamp te vergelijken")
return self.level < other.level
def describe(self):
"""Geeft de beschrijving van een lamp, met de helderheid erachter."""
return super().describe() + f", {self.level}%"
assert DimmableLamp("wit", 0).level == 0
assert DimmableLamp("wit", 100).level == 100
assert DimmableLamp("wit").level == 100
assert DimmableLamp("wit", 30).describe() == "lamp wit: uit, 30%"
Opdracht 5¶
De foutmelding eindigt met ValueError: de helderheid moet van 0 tot en met 100 zijn. De constructor stopt bij raise, dus er komt geen lamp.
lamp = DimmableLamp("wit", 150)
print("gelukt")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[9], line 1
----> 1 lamp = DimmableLamp("wit", 150)
2 print("gelukt")
Cell In[7], line 7, in DimmableLamp.__init__(self, color, level)
4 def __init__(self, color, level=100):
5 """Maak een dimbare lamp; gooit een ValueError bij een onmogelijke level."""
6 if level < 0 or level > 100:
----> 7 raise ValueError("de helderheid moet van 0 tot en met 100 zijn")
8 super().__init__(color)
9 self.level = level
ValueError: de helderheid moet van 0 tot en met 100 zijn
Opdracht 6¶
Het programma drukt geen geldige helderheid af en daarna klaar. gelukt
komt er niet: de constructor gooit, en Python springt meteen naar het
except-blok.
try:
lamp = DimmableLamp("wit", 150)
print("gelukt")
except ValueError:
print("geen geldige helderheid")
print("klaar")
geen geldige helderheid
klaar
Opdracht 7¶
De try staat binnen de lus, om één lamp. Een ongeldige helderheid slaat dan
alleen die lamp over, en de lus gaat door met de volgende. Stond de lus binnen
de try, dan stopte hij bij de eerste fout. De cel drukt overgeslagen: 150
en overgeslagen: -5 af.
def make_lamps(color, levels):
"""Geeft dimbare lampen in color, één per geldige helderheid uit levels."""
lamps = []
for level in levels:
try:
lamps.append(DimmableLamp(color, level))
except ValueError:
print("overgeslagen:", level)
return lamps
lamps = make_lamps("wit", [30, 150, 0, -5, 100])
assert [lamp.level for lamp in lamps] == [30, 0, 100]
assert make_lamps("rood", []) == []
overgeslagen: 150
overgeslagen: -5
Opdracht 8¶
Bij de kaars draait describe van Candle, die een tekst teruggeeft. Bij de
zaklamp draait describe van LightSource, want Torch heeft geen eigen
versie, en die gooit een NotImplementedError met de naam van de klasse:
Torch moet describe overschrijven.
class LightSource:
"""Iets dat licht geeft. Elke subklasse moet describe overschrijven."""
def describe(self):
"""Gooit een NotImplementedError: een subklasse moet dit zelf schrijven."""
name = self.__class__.__name__
raise NotImplementedError(f"{name} moet describe overschrijven")
class Candle(LightSource):
"""Een kaars."""
def describe(self):
"""Geeft een korte beschrijving van de kaars."""
return "kaars"
class Torch(LightSource):
"""Een zaklamp, die vergeet describe te overschrijven."""
print(Candle().describe())
print(Torch().describe())
kaars
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
Cell In[14], line 2
1 print(Candle().describe())
----> 2 print(Torch().describe())
Cell In[13], line 7, in LightSource.describe(self)
4 def describe(self):
5 """Gooit een NotImplementedError: een subklasse moet dit zelf schrijven."""
6 name = self.__class__.__name__
----> 7 raise NotImplementedError(f"{name} moet describe overschrijven")
NotImplementedError: Torch moet describe overschrijven
Opdracht 9¶
True en daarna False. sorted en < gebruiken __lt__, maar > staat
nergens in DimmableLamp. Python draait het dan om: bright > dim wordt
dim < bright, en dat is wel te beantwoorden.
dim = DimmableLamp("wit", 30)
bright = DimmableLamp("wit", 80)
print(bright > dim)
print(dim > bright)
True
False
Opdracht 10¶
De eerste regel drukt False af: __eq__ geeft False bij een ander type. De
tweede regel gooit de TypeError uit __lt__ van opdracht 3.
Zo gaan de operators van deze week om met een argument van een ander type:
__eq__ geeft False, want twee dingen van een ander type zijn gewoon niet
gelijk. De andere operators gooien een TypeError, want op die vraag bestaat
geen antwoord.
print(DimmableLamp("wit", 30) == "wit")
print(DimmableLamp("wit", 30) < Lamp("rood"))
False
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[16], line 2
1 print(DimmableLamp("wit", 30) == "wit")
----> 2 print(DimmableLamp("wit", 30) < Lamp("rood"))
Cell In[7], line 14, in DimmableLamp.__lt__(self, other)
11 def __lt__(self, other):
12 """Geeft True als deze lamp minder fel is dan other."""
13 if not isinstance(other, DimmableLamp):
---> 14 raise TypeError("een DimmableLamp is alleen met een DimmableLamp te vergelijken")
15 return self.level < other.level
TypeError: een DimmableLamp is alleen met een DimmableLamp te vergelijken