Opstap

Deze pagina bevat uitvoerbare code.

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