Opstap

Deze pagina bevat uitvoerbare code.

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