Opstap¶
De uitwerkingen staan in dezelfde volgorde als de twaalf opdrachten in problems/12_opstap.
Opdracht 1¶
a.switch() verandert alleen het attribuut on van a. De uitvoer is True en daarna False.
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)
True
False
Opdracht 2¶
Eén klasse, Lamp, en twee objecten: a en b. Elk object heeft zijn eigen
attribuut on. a.switch() werkt op a, dus binnen die aanroep is self het
object a; b blijft uit.
Opdracht 3¶
De klasse Point hieronder bevat ook de methoden van opdracht 4, 5 en 6.
class Point:
"""Een punt in het vlak."""
def __init__(self, x, y):
"""Maak een punt met de gegeven coördinaten."""
self.x = x
self.y = y
def __repr__(self):
"""Geeft het punt als string, zoals Point(3, 4)."""
return f"Point({self.x}, {self.y})"
def distance_to_origin(self):
"""Geeft de afstand tot het punt (0, 0)."""
return (self.x * self.x + self.y * self.y) ** 0.5
def move(self, dx, dy):
"""Verschuift het punt over dx en dy."""
self.x += dx
self.y += dy
p = Point(3, 4)
assert p.x == 3
assert p.y == 4
Opdracht 4¶
In een f-string komen de waarden van self.x en self.y op hun plek.
assert repr(Point(3, 4)) == "Point(3, 4)"
assert repr(Point(0, -2)) == "Point(0, -2)"
Opdracht 5¶
self is het punt waarvoor de methode wordt aangeroepen, dus self.x en self.y zijn zijn coördinaten.
assert Point(3, 4).distance_to_origin() == 5.0
assert Point(0, 0).distance_to_origin() == 0.0
Opdracht 6¶
move verandert de attributen van het object zelf en heeft daarom geen return. q is een ander object en blijft staan.
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¶
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
s = Square(3)
print(s.side)
3
side is een property zonder setter. Python weigert de toekenning met een AttributeError:
s.side = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[8], line 1
----> 1 s.side = 5
AttributeError: property 'side' of 'Square' object has no setter
Opdracht 8¶
De klasse Square hieronder bevat ook de methode grow van opdracht 9. area is een property, dus je schrijft s.area zonder haakjes.
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
@property
def area(self):
"""De oppervlakte."""
return self._side * self._side
def grow(self, amount):
"""Maakt de zijde amount langer, maar alleen als amount positief is."""
if amount > 0:
self._side += amount
assert Square(3).area == 9
assert Square(0).area == 0
Opdracht 9¶
grow staat in de klasse en mag self._side dus veranderen, maar alleen als amount positief is.
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¶
Van buiten de klasse kan de zijde alleen via grow veranderen, en grow maakt
haar alleen groter. s.side = 1 wordt geweigerd, want side heeft geen setter.
Met s._side = 1 kan het wel: Python houdt dat niet tegen. De _ is een
afspraak dat je zo’n attribuut van buiten de klasse niet gebruikt.
s = Square(3)
s._side = 1
print(s.side)
1
Opdracht 11¶
Figure bewaart de punten in self._points en laat ze zelf verschuiven: move
roept voor elk punt zijn eigen move aan. De klasse Point is die van opdracht 3.
class Figure:
"""Een figuur die uit punten bestaat."""
def __init__(self):
"""Maak een lege figuur."""
self._points = []
def add(self, point):
"""Voeg point toe aan de figuur."""
self._points.append(point)
def move(self, dx, dy):
"""Verschuift elk punt van de figuur over dx en dy."""
for point in self._points:
point.move(dx, dy)
def __repr__(self):
"""Geeft de figuur als string, zoals Figure([Point(0, 0)])."""
return f"Figure({self._points})"
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([])"
f.add(p) zet geen kopie van p in de figuur, maar p zelf. f.move(1, 1)
verschuift dus het punt p, en p.move(-1, -1) verschuift het punt dat in de
figuur zit. Bij een lege figuur loopt de lus in move nul keer.
Opdracht 12¶
q = p maakt geen nieuw punt: q is een tweede naam voor hetzelfde object.
r is een nieuw object, met dezelfde coördinaten.
p = Point(1, 2)
q = p
r = Point(1, 2)
q.move(3, 3)
print(p)
print(r)
print(q is p)
print(r is p)
Point(4, 5)
Point(1, 2)
True
False
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)"
p is verschoven omdat p en q hetzelfde object aanwijzen: q.move(3, 3)
verschuift dat ene object, en p laat het zien. r is een ander object en blijft
staan.
r == p had vóór het verschuiven False gegeven. Point is een eigen klasse, en
bij je eigen objecten vergelijkt == of het hetzelfde object is. r en p
hadden dezelfde waarde, maar niet dezelfde identiteit:
p = Point(1, 2)
r = Point(1, 2)
assert not r == p