Opstap¶
Klassen¶
Leerdoel: een klasse schrijven met een constructor, attributen en methoden, een
attribuut afschermen met _ en @property, een klasse schrijven waarvan de
objecten andere objecten als onderdeel hebben, en met is nagaan of twee namen
hetzelfde object aanwijzen.
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.
Opdracht 1¶
Voorspel eerst wat deze code afdrukt, en voer haar daarna 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
a = Lamp("rood")
b = Lamp("blauw")
a.switch()
print(a.on)
print(b.on)
# voer de code hier uit
Opdracht 2¶
Beantwoord als commentaar in de cel hieronder: hoeveel klassen en hoeveel
objecten komen er in de code van opdracht 1 voor? En waarom drukt b.on iets
anders af dan a.on, terwijl de klasse voor allebei dezelfde is?
# jouw antwoord
Opdracht 3¶
Schrijf een klasse Point met een constructor die twee argumenten krijgt, x en
y, en die opslaat als de attributen self.x en self.y.
Aanroep |
Resultaat |
|---|---|
|
|
|
|
# jouw oplossing
p = Point(3, 4)
assert p.x == 3
assert p.y == 4
Opdracht 4¶
Voeg aan je klasse Point een methode __repr__ toe, zodat een punt wordt
afgedrukt zoals je het zou aanmaken. Voer daarna de cel van opdracht 3 opnieuw
uit, en dan de test hieronder.
Aanroep |
Afgedrukt |
|---|---|
|
|
repr(p) in de test geeft de string die __repr__ teruggeeft.
assert repr(Point(3, 4)) == "Point(3, 4)"
assert repr(Point(0, -2)) == "Point(0, -2)"
Opdracht 5¶
Voeg een methode distance_to_origin(self) toe. Die geeft de afstand van het
punt tot het punt (0, 0): de wortel uit x * x + y * y. Voer daarna de cel
van opdracht 3 opnieuw uit, en dan de test hieronder.
Aanroep |
Resultaat |
|---|---|
|
|
Hint
De wortel van een getal is dat getal tot de macht 0.5: 25 ** 0.5 is 5.0.
Binnen de methode kom je bij de coördinaten via self.
assert Point(3, 4).distance_to_origin() == 5.0
assert Point(0, 0).distance_to_origin() == 0.0
Opdracht 6¶
Voeg een methode move(self, dx, dy) toe. Die verschuift het punt: dx erbij
bij x, en dy erbij bij y. De methode verandert het object en geeft niets
terug. Voer daarna de cel van opdracht 3 opnieuw uit, en dan de test hieronder.
Code |
Daarna |
|---|---|
|
|
p = Point(3, 4)
p.move(1, -4)
assert repr(p) == "Point(4, 0)"
q = Point(3, 4)
assert repr(q) == "Point(3, 4)"
Opdracht 7¶
Neem de klasse Square hieronder over in de cel. Maak een vierkant met
s = Square(3) en druk s.side af.
class Square:
"""Een vierkant waarvan de zijde alleen groter kan worden."""
def __init__(self, side):
"""Maak een vierkant met de gegeven zijde."""
self._side = side
@property
def side(self):
"""De lengte van de zijde, alleen om te lezen."""
return self._side
Voorspel daarna wat s.side = 5 doet, en probeer het. Wat zegt de foutmelding?
# jouw oplossing
Opdracht 8¶
Voeg aan Square een property area toe, die de oppervlakte teruggeeft. Je
schrijft dus s.area, zonder haakjes.
Aanroep |
Resultaat |
|---|---|
|
|
assert Square(3).area == 9
assert Square(0).area == 0
Opdracht 9¶
Voeg een methode grow(self, amount) toe. Die maakt de zijde amount langer,
maar alleen als amount groter is dan 0. Anders gebeurt er niets.
Code |
|
|---|---|
|
|
daarna |
nog steeds |
Hint
grow staat zelf in de klasse, dus daar mag je self._side veranderen.
s = Square(3)
s.grow(2)
assert s.side == 5
s.grow(-4)
assert s.side == 5
s.grow(0)
assert s.side == 5
Opdracht 10¶
Beantwoord als commentaar in de cel hieronder: waarom kan een Square nu nooit
kleiner worden, zolang je hem alleen via zijn methoden en properties gebruikt?
En met welke regel code, van buiten de klasse, zou het tóch kunnen?
# jouw antwoord
Opdracht 11¶
Een figuur bestaat uit punten. Schrijf een klasse Figure die Point-objecten
bevat en ze verschuift via hun eigen methode move. Gebruik je klasse Point
van opdracht 3 tot en met 6.
Methode |
Doet |
|---|---|
|
maakt een lege figuur: de lijst |
|
voegt |
|
verschuift elk punt van de figuur over |
|
geeft de figuur als string, zoals |
Code |
Daarna |
|---|---|
|
|
daarna |
|
Hint
move van Figure roept voor elk punt in self._points de methode move
van dat punt aan. Voor __repr__ helpt het dat een lijst wordt afgedrukt met
de __repr__ van elk element: repr([Point(0, 0)]) is "[Point(0, 0)]".
# jouw oplossing
Voorspel eerst, voordat je de test hieronder leest of uitvoert. Schrijf als
commentaar in de cel hieronder op: wat is p na f.move(1, 1)? En wat is f
als je daarna p.move(-1, -1) aanroept? Voer pas daarna de test uit.
# jouw voorspelling
p = Point(0, 0)
q = Point(3, 4)
f = Figure()
f.add(p)
f.add(q)
assert repr(f) == "Figure([Point(0, 0), Point(3, 4)])"
f.move(1, 1)
assert repr(f) == "Figure([Point(1, 1), Point(4, 5)])"
assert repr(p) == "Point(1, 1)"
p.move(-1, -1)
assert repr(f) == "Figure([Point(0, 0), Point(4, 5)])"
empty = Figure()
empty.move(1, 1)
assert repr(empty) == "Figure([])"
Beantwoord als commentaar in de cel hieronder: waarom is p ook verschoven,
terwijl je alleen f.move hebt aangeroepen? En waarom ziet f het, als je
p van buiten de figuur verschuift?
# jouw antwoord
Opdracht 12¶
Een punt kan twee namen hebben. Schrijf in de cel hieronder, met je klasse
Point van opdracht 3 tot en met 6:
een punt
pop(1, 2);een tweede naam
qvoor hetzelfde punt;een nieuw punt
r, ook op(1, 2);een aanroep die het punt via
qverschuift over3en3.
Hint
Een nieuw object ontstaat alleen als je de klasse aanroept.
# jouw oplossing
Voorspel eerst, voordat je de test hieronder leest of uitvoert. Schrijf als
commentaar in de cel hieronder op: wat drukken print(p) en print(r) af? En
wat geven q is p en r is p? Voer pas daarna je cel en de test uit.
# jouw voorspelling
assert q is p
assert r is not p
assert repr(p) == "Point(4, 5)"
assert repr(q) == "Point(4, 5)"
assert repr(r) == "Point(1, 2)"
Beantwoord als commentaar in de cel hieronder: waarom is p verschoven,
terwijl je move via q hebt aangeroepen? En vóór het verschuiven had r
dezelfde coördinaten als p: wat had r == p toen gegeven, en waarom?
# jouw antwoord