← Python/Eigen typen
Les 4 van 8
MODULE 07 · 04◷ 28 min

Een property bewaakt de geldige toestand

Na deze les kun je
  • Lezen en schrijven aan een property koppelen.
  • Een invariant bij initialisatie en latere wijzigingen bewaken.
  • De vorige toestand bij een geweigerde wijziging behouden.

Een voorraad kan niet zomaar negatief worden. Je wilt de huidige waarde eenvoudig kunnen lezen, terwijl iedere wijziging dezelfde controle doorloopt. Een property verbindt de vertrouwde attribuutnotatie met code die deze afspraak bewaakt.

Lezen via een getter

Een property laat je item.amount schrijven terwijl er een methode wordt uitgevoerd. De methode die de waarde leest heet de getter. Met de decoratornotatie @property geef je aan dat de volgende methode deze leeshandeling beschrijft.

De getter heeft self als parameter en geeft bijvoorbeeld self._amount terug. De aanroeper gebruikt geen ronde haakjes: het blijft item.amount. De algemene werking van decorators behandelen we later; hier gebruik je alleen deze vaste vorm voor een attribuut.

Schrijven via een setter

Een setter beschrijft wat er bij een toekenning moet gebeuren. Met @amount.setter koppel je haar aan de bestaande property amount. De methode krijgt self en de nieuwe waarde, controleert die en bewaart haar daarna.

Het voorbeeld gebruikt een snelheid. Voorspel welke regel de controle uitvoert wanneer de snelheid wordt aangepast. De buitenkant van het object gebruikt steeds dezelfde eenvoudige puntnotatie.

Toekennen via een property met setter
class Speed:
    def __init__(self, speed=0):
        self.speed = speed

    @property
    def speed(self):
        return self._speed

    @speed.setter
    def speed(self, value):
        if value < 0:
            raise ValueError("snelheid mag niet negatief zijn")
        self._speed = value

item = Speed(5)
print(item.speed)
item.speed = 12
print(item.speed)
Verwachte uitvoer
5
12

De naam met een onderstreping, zoals _speed, markeert hier de interne opslag volgens afspraak. Python maakt dat attribuut daarmee niet ontoegankelijk. De property bewaakt de afgesproken publieke route; een onderstreping is geen beveiligingsgrens.

Controleer vóór de wijziging

Een invariant is een afspraak die voor een bruikbaar object geldig moet blijven. Voor Stock is dat een hoeveelheid van nul of hoger. Een geweigerde verandering mag de vorige geldige voorraad niet eerst beschadigen.

Doe de controle daarom voordat je de nieuwe waarde opslaat. Werp bij een ongeldige waarde de afgesproken ValueError. De foutafhandeling uit de vorige module helpt de aanroeper reageren, terwijl jouw object zijn oude toestand behoudt.

Een geweigerde wijziging laat de oude waarde intact
class Speed:
    def __init__(self, speed=0):
        self.speed = speed

    @property
    def speed(self):
        return self._speed

    @speed.setter
    def speed(self, value):
        if value < 0:
            raise ValueError("snelheid mag niet negatief zijn")
        self._speed = value

item = Speed(12)
try:
    item.speed = -3
except ValueError as error:
    print(error)
print(item.speed)
Verwachte uitvoer
snelheid mag niet negatief zijn
12

De getoonde snelheid na de afwijzing maakt zichtbaar of de controle op tijd gebeurde. Alleen een foutmelding printen bewaakt de toestand niet. Ook een fout werpen na de toekenning zou de ongeldige waarde kunnen achterlaten.

Beginwaarden volgen dezelfde regel

Een nieuwe instantie moet meteen aan haar invariant voldoen. Laat __init__ de beginwaarde daarom via dezelfde property instellen. Zo doorloopt een negatieve beginwaarde dezelfde controle als een latere negatieve toekenning.

Je hoeft de regel dan niet op twee plaatsen te kopiëren. Een verandering in de validatie kan via één publieke route werken. De interne opslag rechtstreeks vullen zou die controle bij het maken van het object overslaan.

Veelgemaakte fout: de setter roept zichzelf aan

Binnen de setter opnieuw self.amount = value uitvoeren gebruikt weer dezelfde setter. Die blijft zichzelf daardoor aanroepen. Bewaar de gecontroleerde waarde op het interne attribuut, bijvoorbeeld self._amount, zodat de handeling kan eindigen.

De getter leest ook dat interne attribuut. Getter en setter delen daarmee hun opslag, maar hebben verschillende verantwoordelijkheden. Eén methode berekent de zichtbare waarde; de andere bewaakt de toegestane wijziging.

Jouw voorraadcontract

Nul is een geldige voorraad, zowel bij het maken als bij een toekenning. take neemt een positief aantal af en geeft de resterende voorraad terug. Nul of een negatief afnamebedrag wordt geweigerd; meer afnemen dan beschikbaar is krijgt een eigen foutreden.

Bij iedere afwijzing blijft de oude hoeveelheid intact. De controles lezen de echte property ook na de geworpen fout, zodat een passende fouttekst alleen niet voldoende is. Voeg geen bovenlimiet of automatische afronding toe.

AAN DE SLAG 0/4
1

Een echte leesproperty

Maak Stock.amount als property die de interne _amount leest. Zij geeft de actuele gehele hoeveelheid terug; nul is geldig. De buitenkant gebruikt stock.amount zonder haakjes.

2

Een wijziging vooraf bewaken

Maak de setter van amount: accepteer nul en positieve gehele getallen en sla ze op _amount op. Werp bij een negatief getal ValueError met voorraad moet nul of hoger zijn en laat de oude hoeveelheid intact.

3

Ook de beginwaarde valideren

Vul __init__(amount=0) in door de beginwaarde via dezelfde amount-property in te stellen. Nul is de standaard; negatieve beginwaarden moeten dezelfde ValueError geven. Omzeil de setter dus niet met directe interne opslag.

4

Een begrensde afname

Maak take(number): weiger nul of een negatief getal met ValueError en afname moet positief zijn. Weiger meer dan beschikbaar met ValueError en onvoldoende voorraad. Geef na een geldige afname de nieuwe hoeveelheid terug; iedere afwijzing laat de oude voorraad intact.

Onthoud dit

  • Een property koppelt lezen en schrijven aan getter- en settercode.
  • Controleer nieuwe waarden voordat je de bestaande toestand verandert.
  • Laat ook de initialisatie de invariant via dezelfde route bewaken.

Hierna laat je een dataclass een deel van de terugkerende gegevenscode schrijven.

main.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.