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.
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.
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.
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.
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)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.
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.
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)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.
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.
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.
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.
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.
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.
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.
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.
Hierna laat je een dataclass een deel van de terugkerende gegevenscode schrijven.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.