- Leren
- Informatica & ICT
- Programmeren
- Inleiding tot programmeren met Python
- Klassen en objecten in Python
Klassen en objecten in Python: objectgeoriënteerd programmeren
==, sorteren en +. Bouw programma’s waarin objecten andere objecten bevatten en aanroepen, en beoordeel wanneer een klasse een programma duidelijker maakt dan functies en dictionary’s.BeginnerLes 10 van 10 in Cursus Inleiding tot programmeren met Python
Wat je leert
- Ontdek dat elke Pythonwaarde een object is met eigen gegevens en methoden. Schrijf vervolgens zelf een klasse en maak er objecten van.
- Herken de objecten die je al gebruiktBeschrijf strings, lijsten en dictionary’s als objecten die een type hebben, gegevens bevatten en methoden bieden die je met puntnotatie aanroept.
- Definieer een klasse en maak instantiesSchrijf een klasse-instructie, maak er meerdere afzonderlijke objecten van en stel hun attributen in en lees ze met puntnotatie.
- Herken de objecten die je al gebruikt
- Gebruik een constructor om elk nieuw object de nodige attributen te geven, kies tussen instantie- en klasseattributen en voorkom dat ongeldige objecten worden gemaakt.
- Schrijf een constructor met __init__ en selfDefinieer
__init__zodat elk nieuw object begint met zijn eigen attributen, afkomstig uit de argumenten of ingesteld op beginwaarden. - Kies tussen instantieattributen en klasseattributenBepaal of een waarde bij elk object afzonderlijk of bij de klasse als geheel hoort en voorspel hoe Python beide soorten vindt en wijzigt.
- Zorg dat objecten vanaf het begin geldig blijvenControleer de argumenten die een constructor ontvangt, werp een duidelijke exceptie op bij ongeldige waarden en handhaaf de regels die een object betekenis geven.
- Schrijf een constructor met __init__ en self
- Schrijf methoden die de gegevens van een object gebruiken en wijzigen, en bescherm die gegevens tegen wijzigingen die het object ongeldig maken.
- Definieer instantiemethoden en roep ze aanSchrijf methoden die met
selfwerken, roep ze aan op objecten en geef resultaten terug in plaats van ze af te drukken. - Wijzig de toestand van een object met methodenSchrijf methoden die attributen bijwerken terwijl het object geldig blijft, en voorspel wanneer een wijziging zichtbaar is via andere namen voor hetzelfde object.
- Bescherm attributen met naamgevingsconventies en propertiesMarkeer attributen als intern met een laag streepje en gebruik properties om nieuwe waarden te controleren of om waarden te berekenen die actueel blijven.
- Definieer instantiemethoden en roep ze aan
- Schrijf speciale methoden waarmee je objecten leesbare tekst tonen, met
==en<worden vergeleken en met operatoren en ingebouwde functies werken.- Geef objecten leesbare stringrepresentatiesSchrijf
__str__en__repr__zodat objecten bruikbare tekst tonen wanneer ze worden afgedrukt, bekeken of in een lijst opgeslagen. - Vergelijk en combineer objecten met operatorenDefinieer
__eq__,__lt__,__add__en__len__zodat je objecten werken met==, sorteren,+enlen(), en bepaal wanneer een operator duidelijker is dan een methode met een naam.
- Geef objecten leesbare stringrepresentaties
- Combineer objecten zodat elk object zijn eigen gegevens verwerkt, volg programma’s waarin objecten elkaar aanroepen en bepaal wanneer een klasse een programma duidelijker maakt.
- Maak objecten die andere objecten bevattenSla objecten op als attributen of in lijsten binnen een ander object en schrijf methoden die op de hele collectie werken.
- Laat objecten samenwerken via methodenGeef objecten door aan methoden en functies, geef nieuwe objecten terug en volg een kort programma waarin meerdere objecten samenwerken.
- Bepaal wanneer een klasse een programma duidelijker maaktBeoordeel wanneer het helpt om gegevens en gedrag in een klasse te bundelen, ontwerp een kleine klasse op basis van een beschrijving en herschrijf code met dictionary’s als een klasse.
- Maak objecten die andere objecten bevatten
Klassen en objecten in Python: uitleg
1Begrijp objecten en definieer je eerste klasse
Objecten komen al voor in elk Pythonprogramma dat je schrijft: een string heeft methoden, een lijst verandert haar inhoud en een dictionary geeft antwoord op zoekopdrachten. Door die bekende bewerkingen als objectgedrag te lezen, krijg je een vertrekpunt voor je eigen klassen. Leer hoe typen, identiteit en gedeelde referenties een programma beïnvloeden. Definieer daarna een kleine klasse en geef afzonderlijke instanties hun eigen attributen.
1.1 Herken de objecten die je al gebruikt
Je hebt al objecten gebruikt wanneer je met een getal, string, lijst of dictionary werkte. Een object heeft een type, een waarde of toestand en bewerkingen die het ondersteunt. Zelfs functies en None zijn objecten. Als je je eigen klasse schrijft, breid je dit bekende systeem uit.
Zoek het type en de methoden van een object
type("hello") geeft <class 'str'> terug; type([1, 2]) geeft <class 'list'> terug. De woorden type en klasse beschrijven hier dezelfde rol. isinstance(3.5, float) is True, terwijl isinstance("3.5", float) False is: de tweede waarde is tekst, ook al lijkt die op een getal.
Een methode is een functie die een type biedt. In "ada lovelace".title() is de string vóór de punt het object, is title de methode en roepen de ronde haakjes die aan. Het resultaat is "Ada Lovelace". dir(value) geeft een lijst met namen die een object biedt; namen zoals __len__ zijn speciale methoden die objecten verbinden met Pythonbewerkingen zoals len(value).
Volg het resultaat en het oorspronkelijke object
Een methodeaanroep kan een resultaat teruggeven, een object wijzigen of beide doen. Controleer het gedrag van de methode voordat je beslist wat je toekent.
word = "level"
print(word.count("l")) # 2
print(word.upper()) # LEVEL
print(word) # level
nums = [3, 1, 2]
result = nums.sort()
print(nums) # [1, 2, 3]
print(result) # None
Strings zijn onwijzigbaar: hun inhoud kan niet veranderen. upper() geeft een andere string terug, dus word behoudt zijn oorspronkelijke waarde. Om het resultaat in hoofdletters onder die naam te bewaren, schrijf je word = word.upper().
Lijsten zijn wijzigbaar. sort() wijzigt de lijst zelf en geeft None terug. Als je nums = nums.sort() schrijft, verliest nums de referentie naar de lijst en krijgt het de waarde None. Gebruik nums.sort() op een eigen regel of nums = sorted(nums) om een nieuwe gesorteerde lijst te krijgen. Bij een dictionary geeft {"a": 1}.get("b", 0) 0 terug omdat de gevraagde sleutel ontbreekt.
Ook ronde haakjes zijn van belang. word.upper is een methodeobject; word.upper() voert het uit. Veel objecten kunnen hetzelfde gedrag bieden: "cat".upper() en "dog".upper() gebruiken dezelfde soort methode met verschillende stringgegevens.
Maak onderscheid tussen gelijkheid en identiteit
Gelijkheid gaat over de vraag of waarden overeenkomen. Identiteit gaat over de vraag of twee namen naar één object verwijzen.
a = [1, 2]
b = [1, 2]
c = a
print(a == b) # True
print(a is b) # False
print(c is a) # True
c.append(3)
print(a) # [1, 2, 3]
print(b) # [1, 2]
a en b beginnen met een gelijke inhoud, maar zijn afzonderlijke lijsten. c = a maakt een alias, een andere naam voor dezelfde lijst. Toevoegen via c wijzigt het object waar ook a naar verwijst. Het wijzigt niet de afzonderlijke lijst die b bevat. Teken bij het volgen van een programma één vak per object en een pijl van elke naam naar het bijbehorende vak.
Belangrijkste punten
- Elke Pythonwaarde is een object met een type, toestand en ondersteunde bewerkingen.
- Roep een methode aan met ronde haakjes; alleen een referentie naar een methode voert die niet uit.
- Controleer of een methode een nieuwe waarde teruggeeft of het bestaande object wijzigt.
==vergelijkt waarden;isvergelijkt identiteit.- Door een toekenning kunnen twee namen verwijzen naar hetzelfde wijzigbare object.
1.2 Definieer een klasse en maak instanties
Een klasse definieert een type dat je in je eigen programma kunt gebruiken. Elk object dat ervan wordt gemaakt, is een instantie. Eén klasse beschrijft de soort; instanties bevatten de gegevens van afzonderlijke dingen. Een leeg formulier en de ingevulde exemplaren zijn een bruikbare vergelijking.
Schrijf een klasse en roep die aan
Een klasse-instructie heeft het gereserveerde woord class, een naam, een dubbele punt en een ingesprongen inhoud. Met pass geef je een lege inhoud op terwijl je de structuur uitprobeert.
class Book:
pass
b1 = Book()
b2 = Book()
print(b1 is b2) # False
print(isinstance(b1, Book)) # True
Elke aanroep van Book() maakt een afzonderlijke instantie. In een script toont type(b1) <class '__main__.Book'>. De klasse-instructie moet worden uitgevoerd voordat code Book probeert te gebruiken. Je kunt een klasse ook in een module definiëren en met from library import Book importeren, waarna je instanties maakt in het aanroepende programma.
Volg de naamgevingsconventies van Python: Book, BankAccount en ShoppingCart gebruiken CapWords, waarbij elk woord met een hoofdletter begint. Namen van variabelen en methoden gebruiken kleine letters met lage streepjes tussen de woorden, zoals book_count en add_item. Gebruik vier spaties voor elk inspringniveau.
Sla gegevens op elke instantie op
Een attribuut is een benoemde waarde die je met een punt benadert. Door een waarde aan een attribuut toe te kennen, sla je die op; door dezelfde naam te lezen, haal je die op.
b1.title = "Dune"
b2.title = "Emma"
b1.pages = 412
print(b1.title) # Dune
print(b2.title) # Emma
print(b1.pages) # 412
De gegevens horen bij de gekozen instantie. Als je b1 een attribuut pages geeft, krijgt b2 dat niet. Het lezen van b2.pages werpt nu AttributeError op, omdat dat object geen attribuut met de naam pages heeft.
Toekenningen kunnen nieuwe namen maken zonder te controleren wat je bedoelde. Na b1.titel = "X" bevat het oorspronkelijke b1.title nog steeds "Dune"; het object heeft er een tweede attribuut met een verkeerd gespelde naam bij gekregen. Als een waarde niet lijkt te veranderen, vergelijk dan de spelling op de plek waar de waarde is toegekend en waar die wordt gelezen.
Houd de klasse en haar instanties uit elkaar
| Expressie | Betekenis |
|---|---|
Book | Het klasseobject dat het type definieert |
Book() | Een aanroep die een instantie maakt |
b1.title | De titel die op dit boek is opgeslagen |
Book.title | Een attribuut dat op de klasse zelf wordt opgevraagd |
b = Book geeft de klasse een andere naam. Het maakt geen boek. Als titels alleen op instanties zijn ingesteld, werpt het opvragen van Book.title AttributeError op; er is geen enkele titel die door het type wordt gedeeld.
Geef elk object een betrouwbaar begin
Attributen van buitenaf instellen is mogelijk, maar daardoor raakt de inrichting verspreid over het programma. Het ene boek krijgt misschien een titel en een aantal pagina’s, terwijl het andere alleen een titel krijgt. Latere code kan er niet veilig van uitgaan dat beide pages hebben.
Een inrichtingsmethode met de naam __init__ plaatst de begintoekenningen binnen de klasse. Elke aanroep om een object te maken volgt dan hetzelfde inrichtingsproces. De klasse kan een titel verplicht stellen en andere attributen met zinvolle beginwaarden maken, zodat de aanroepende code geen afzonderlijke reeks toekenningen hoeft te onthouden.
Belangrijkste punten
- Een klasse definieert een type; een instantie is één object dat ervan is gemaakt.
- Gebruik
ClassName()om een instantie te maken en puntnotatie om haar attributen te benaderen. - Elke instantie kan onder dezelfde attribuutnamen andere gegevens bevatten.
- Een ontbrekend attribuut werpt
AttributeErrorop; een toekenning met een verkeerd gespelde naam kan een nieuw attribuut maken.
2Richt objecten in met constructors en attributen
Een object heeft een betrouwbare begintoestand nodig voordat zijn methoden nuttig werk kunnen doen. Initialisatie verbindt de argumenten in een klasseaanroep met attributen die op de nieuwe instantie worden opgeslagen. Kies welke waarden bij één object horen en welke bij de klasse, en valideer invoer voordat je die accepteert. Door opzoekingen en fouten bij de inrichting te volgen, herken je onbedoeld delen en ontbrekende gegevens.
2.1 Schrijf een constructor met __init__ en self
Elke nieuw gemaakte rekening heeft een eigenaar en een beginsaldo nodig. Door die inrichting in __init__ te zetten, krijgt elk object de attributen die latere methoden verwachten. Python maakt eerst de instantie en roept daarna __init__ aan om die te initialiseren.
Verbind argumenten met attributen
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
self.transactions = []
acct = BankAccount("Maya", 50)
other = BankAccount("Leo")
Bij de eerste aanroep geeft Python de nieuwe rekening door als self, "Maya" als owner en 50 als balance. Door de toekenningen wordt acct.owner gelijk aan "Maya" en acct.balance aan 50. De tweede aanroep gebruikt het standaardsaldo, dus other.balance is 0.
self is de parameter die het huidige object ontvangt. Het is een conventie, geen gereserveerd woord in Python. Gebruik deze naam zodat lezers het object meteen herkennen. Aanroepende code laat deze parameter weg: BankAccount("Maya", 50) geeft twee argumenten door, geen drie.
Houd lokale namen en opgeslagen gegevens apart
In self.owner = owner is de naam rechts een lokale parameter. De naam links is een attribuut dat op het object wordt opgeslagen. Wanneer de initialisatie eindigt, kunnen andere methoden self.owner nog steeds gebruiken.
Als je balance = balance schrijft, ken je alleen opnieuw een waarde toe aan de lokale naam. Er ontstaat geen attribuut, dus het latere lezen van acct.balance mislukt. Zet self. vóór elk attribuut dat deel van het object moet blijven.
Plan de initialisatie door op een rij te zetten wat elke instantie nodig heeft. Verplichte waarden worden parameters na self; optionele waarden kunnen standaardwaarden krijgen. Waarden die altijd hetzelfde beginnen, kun je in de inhoud toekennen, zoals self.visits = 0. Een lijst die met self.transactions = [] wordt gemaakt, wordt bij elke aanroep opnieuw aangemaakt.
Voorkom een gedeelde standaardlijst
Een standaardargument wordt geëvalueerd wanneer Python de methode definieert. Met def __init__(self, items=[]) ontvangen aanroepen die items weglaten dezelfde lijst. Als de initialisatiemethode die lijst rechtstreeks opslaat, verandert een wijziging van de elementen van het ene object ook de elementen die een ander object ziet.
Gebruik None als standaardwaarde en maak bij elke aanroep een nieuwe lijst:
class Cart:
def __init__(self, items=None):
self.items = [] if items is None else list(items)
a = Cart()
b = Cart()
a.items.append("pen")
print(b.items) # []
list(items) maakt ook een nieuwe buitenlijst wanneer de aanroepende code een lijst doorgeeft. De objecten in die lijst worden nog steeds via referenties gedeeld; door de lijst te maken, wordt niet elk element opnieuw gemaakt.
Lees fouten bij de inrichting
Bij BankAccount() ontbreekt het verplichte argument owner en wordt TypeError opgeworpen. Vergelijk de aanroep met de parameters na self.
De spelling __init__ heeft aan beide kanten twee lage streepjes. _init_ is een gewone methodenaam en voert deze inrichting niet automatisch uit. __init__ moet None teruggeven, dus laat je een return-instructie normaal gesproken weg. Als je een saldo teruggeeft, wordt TypeError opgeworpen; de klasseaanroep zelf geeft de nieuwe instantie aan de aanroepende code.
Belangrijkste punten
__init__initialiseert de instantie nadat Python die heeft gemaakt.- Python levert
self; de aanroepende code levert de overige argumenten. self.name = nameslaat een parameter op als instantieattribuut.- Maak lijsten per instantie tijdens de initialisatie; gebruik
Nonein plaats van een wijzigbare standaardwaarde. __init__moetNoneteruggeven.
2.2 Kies tussen instantieattributen en klasseattributen
Sommige waarden beschrijven één object; andere beschrijven het type als geheel. De grootte van een pizza hoort bij die pizza. Een maximum voor het beleg of een telling van gemaakte pizza’s kan bij de klasse horen. De plaats van een toekenning bepaalt welk soort attribuut die maakt.
Zet de waarde op de juiste plek
class Pizza:
MAX_TOPPINGS = 5
count = 0
def __init__(self, size):
self.size = size
self.toppings = []
Pizza.count += 1
p1 = Pizza("large")
p2 = Pizza("small")
size en toppings zijn instantieattributen, ingesteld op self. Elke pizza heeft zijn eigen waarden. MAX_TOPPINGS en count zijn klasseattributen, toegekend in de klasse-inhoud buiten een methode. Hoofdletters markeren een waarde die als constante is bedoeld; ze voorkomen geen hernieuwde toekenning.
Na deze aanroepen is Pizza.count gelijk aan 2. Elke initialisatiemethode werkt hetzelfde klasseattribuut bij. p1.size is "large", terwijl p2.size "small" is.
Volg opzoeking en naamverhulling
Bij gewone gegevensattributen zoals deze controleert Python bij het lezen van p1.MAX_TOPPINGS eerst de instantie en daarna de klasse. Omdat op p1 geen waarde is opgeslagen, vindt Python Pizza.MAX_TOPPINGS, met de waarde 5.
Een toekenning via een instantie heeft een ander effect:
p1.MAX_TOPPINGS = 8
Pizza.MAX_TOPPINGS = 6
print(p1.MAX_TOPPINGS) # 8
print(p2.MAX_TOPPINGS) # 6
print(Pizza.MAX_TOPPINGS) # 6
De eerste regel maakt een instantieattribuut op p1. Dat verhult de klassewaarde voor dat object. Een latere wijziging van de klassewaarde bereikt p2, dat nog steeds van de klasse leest, maar vervangt de eigen waarde van p1 niet.
| Toekenning | Wat verandert |
|---|---|
self.size = size | Gegevens op de huidige instantie |
Pizza.count += 1 | De gedeelde klasseteller |
p1.MAX_TOPPINGS = 8 | Een instantiewaarde die de klassewaarde verbergt |
Let op twee vormen van onbedoeld delen
Als de initialisatiemethode self.count += 1 zou gebruiken, zou Python de 0 van de klasse lezen, er één bij optellen en een instantieattribuut opslaan. Elke nieuwe pizza zou zijn eigen count van 1 krijgen, terwijl Pizza.count 0 bleef. Gebruik de klassenaam wanneer je een teller voor de hele klasse bijwerkt.
Een lijst in de klasse-inhoud veroorzaakt een ander probleem. Met toppings = [] vinden instanties die geen afzonderlijk attribuut voor beleg hebben allemaal dezelfde lijst. p1.toppings.append("olives") wijzigt die gedeelde lijst; het kent geen nieuw instantieattribuut toe. Zet self.toppings = [] in __init__ wanneer elke pizza zijn eigen beleg nodig heeft.
Bekijk de eigen gegevens van een object
Voor de oorspronkelijke p2 geeft vars(p2) {'size': 'small', 'toppings': []}. De klassewaarden ontbreken omdat ze op Pizza staan. Dit is nuttig bij het debuggen van een onverwacht opzoekresultaat: controleer of de instantie een eigen waarde heeft en bekijk daarna de klassewaarde. Een gedeelde instelling hoort op de klasse; een naam, grootte, saldo of persoonlijke collectie hoort op de instantie.
Belangrijkste punten
- Instantieattributen bevatten gegevens voor één object; klasseattributen bevatten gedeelde instellingen of gegevens voor de hele klasse.
- Een instantiegegevensattribuut kan een klasseattribuut met dezelfde naam verhullen.
- Werk een gedeelde teller bij via de klassenaam.
- Als je een lijst op klasseniveau wijzigt, wijzig je de ene gedeelde lijst.
vars(obj)toont de eigen opgeslagen attributen, niet de klasseattributen die het kan vinden.
2.3 Zorg dat objecten vanaf het begin geldig blijven
Een object moet beginnen in een toestand die zijn methoden kunnen gebruiken. Een rechthoek met een negatieve breedte kan de bedoelde vorm niet voorstellen. Een regel die voor elk object van een klasse waar moet blijven, is een invariant. Validatie controleert die regels voordat gegevens worden geaccepteerd.
Controleer vóór het opslaan
class Rectangle:
def __init__(self, width, height):
if width <= 0:
raise ValueError("width must be positive")
if height <= 0:
raise ValueError("height must be positive")
self.width = width
self.height = height
Rectangle(3, 4) doorstaat beide controles en geeft een geïnitialiseerd object terug. Rectangle(-2, 4) werpt bij de eerste controle ValueError op. De latere toekenningen worden niet uitgevoerd. Als het programma die exceptie niet opvangt, stopt de uitvoering daar.
Stel dat r al naar een geldige rechthoek verwijst. In r = Rectangle(-2, 4) mislukt de aanroep rechts vóór de toekenning. r behoudt zijn eerdere waarde. Als r vóór de mislukte aanroep geen waarde had, maakt de toekenning er ook geen aan.
Kies een bruikbare exceptie
| Exceptie | Probleem dat ze beschrijft |
|---|---|
ValueError | Een aanvaardbare soort invoer heeft een onaanvaardbare waarde |
TypeError | De invoer heeft het verkeerde type voor de bewerking |
Een negatieve numerieke breedte is een waardefout. Tekst die wordt doorgegeven waar een getal nodig is, is een typefout. De vergelijking met 0 in dit voorbeeld werpt voor tekst al TypeError op. Een klasse kan een type ook expliciet met isinstance controleren als een duidelijkere melding nodig is.
Schrijf meldingen die de regel noemen, zoals "width must be positive". Bij een melding met alleen "bad input" moet de aanroepende code raden welke waarde moet worden hersteld.
Laat de aanroepende code het herstel afhandelen
while True:
try:
width = float(input("Width: "))
room = Rectangle(width, 3)
break
except ValueError as error:
print("Try again:", error)
Invoer van abc mislukt bij float; invoer van -2 doorstaat de conversie maar mislukt in Rectangle. Beide werpen ValueError op, dus deze lus handelt beide af. De klasse stelt haar gegevensregel vast, terwijl de aanroepende code beslist om opnieuw om invoer te vragen.
Een waarschuwing afdrukken binnen __init__ wijst de aanroep niet af. Als de uitvoering doorgaat, kan het programma een ongeldig object ontvangen en later bij het gebruik ervan mislukken. Een exceptie opwerpen stopt de mislukte inrichting waar het probleem optreedt.
Schoon gegevens bewust op en handhaaf de regel
Een gedocumenteerde opschoning kan nuttig zijn. self.name = name.strip() verwijdert spaties aan de randen; valideer de opgeschoonde naam als lege namen verboden zijn. self.items = list(items) maakt een afzonderlijke buitenlijst, zodat toevoegen aan de lijst van de aanroepende code niets aan deze lijst toevoegt. Een ongeldige breedte veranderen in haar absolute waarde zou de betekenis van de invoer veranderen en vereist dus een expliciete ontwerpregel.
Initialisatiecontroles worden uitgevoerd bij het maken van het object. Een gewone toekenning zoals r.width = -5 omzeilt ze later nog steeds. Methoden die een breedte wijzigen, moeten die opnieuw controleren. Een property kan een controle op de attribuuttoekenning zelf plaatsen, zodat dezelfde regel tijdens de inrichting en bij latere wijzigingen wordt uitgevoerd.
Belangrijkste punten
- Een invariant is een regel waaraan de toestand van een geldig object moet voldoen.
- Valideer voordat je gegevens accepteert en werp bij een fout een duidelijke exceptie op.
- Een mislukte aanroep om een object te maken kent geen nieuw object toe aan de naam van de aanroepende code.
- Controles in de constructor hebben ook ondersteuning nodig in latere methoden of properties die de toestand wijzigen.
3Geef objecten gedrag met instantiemethoden
Methoden zetten opgeslagen gegevens om in nuttig gedrag. Een rechthoek kan zijn oppervlakte berekenen en een rekening kan zijn saldo bijwerken terwijl een transactie wordt vastgelegd. Volg hoe Python het ontvangende object aan elke methode doorgeeft en maak daarna onderscheid tussen een teruggegeven resultaat en een wijziging van de toestand. Met properties kan een klasse leesbare toegang tot attributen behouden en tegelijk toekenningen controleren of waarden op aanvraag berekenen.
3.1 Definieer instantiemethoden en roep ze aan
Een instantiemethode is een functie die binnen een klasse is gedefinieerd en werkt met het object dat als self wordt doorgegeven. Het object levert zijn opgeslagen gegevens; extra parameters leveren eventuele aanvullende informatie die voor de actie nodig is.
Lees attributen via self
Voeg deze methoden toe binnen de gevalideerde klasse Rectangle, op hetzelfde inspringniveau als __init__:
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def is_square(self):
return self.width == self.height
Met r = Rectangle(3, 4) geeft r.area() 12 terug, geeft r.perimeter() 14 terug en geeft r.is_square() False terug. Elke methode leest de attributen van hetzelfde object. Een tweede rechthoek gebruikt dezelfde methodedefinities met zijn eigen breedte en hoogte.
Binnen een methode duidt self.width een attribuut aan. Een losse width duidt een lokale of andere toegankelijke variabele aan; Python zoekt die niet automatisch op het huidige object. Als zo’n variabele niet bestaat, werpt return width * height NameError op. Door self. te schrijven, maak je expliciet bij welk object de bedoelde gegevens horen.
Volg de aanroep
Voor deze klasse werkt r.area() als Rectangle.area(r). Python vindt de functie op de klasse en levert r als eerste argument. Een methodereferentie zoals r.area is een gebonden methode: die onthoudt de functie en deze rechthoek. Door () toe te voegen, voer je de aanroep uit.
Als een methode is gedefinieerd als def resize(self, factor), roep je die aan als r.resize(2). r vult self in en 2 vult factor in. Je geeft r niet nogmaals door. Als een instantiemethode per ongeluk als def area(): is geschreven, werpt een aanroep op r TypeError op, omdat Python het object nog steeds doorgeeft aan een functie die geen argumenten accepteert.
De ene methode kan via self een andere gebruiken:
def double_area(self):
return self.area() * 2
Hier berekent self.area() met dezelfde rechthoek. Als je self. weglaat, vraag je om een afzonderlijke functie met de naam area, niet om de methode van het object.
Geef resultaten terug aan de aanroepende code
Een berekeningsmethode hoort haar resultaat normaal gesproken terug te geven. Zo krijgt de aanroepende code een waarde om op te slaan, te combineren, te toetsen of weer te geven:
r1 = Rectangle(3, 4)
r2 = Rectangle(2, 5)
total = r1.area() + r2.area()
print(total) # 22
Als area het antwoord alleen zou afdrukken, zou elke aanroep None teruggeven. De optelling zou dan mislukken met TypeError, ook al waren er twee getallen op het scherm verschenen. Afdrukken is een actie; teruggeven levert een waarde aan de expressie die de aanroep bevat.
Gebruik methodenamen die de taak laten zien. deposit en resize duiden acties aan; is_square en has_room suggereren booleaanse controles. Een methode heeft geen parameters nodig voor gegevens die al op haar object zijn opgeslagen. Ze heeft wel parameters nodig voor nieuwe invoer, zoals een vergrotingsfactor of een stortingsbedrag.
Belangrijkste punten
- Zet
selfvooraan in een instantiemethode; Python levert het ontvangende object. - Lees attributen en roep andere methoden aan via
self.. - Ronde haakjes voeren een gebonden methode uit.
- Geef berekende waarden terug zodat aanroepende code ze kan combineren, toetsen of weergeven.
3.2 Wijzig de toestand van een object met methoden
De toestand van een object is de huidige verzameling waarden in zijn attributen. Een uitleesmethode geeft informatie over die toestand. Een wijzigingsmethode verandert die. Een bruikbare wijzigingsmethode controleert haar invoer en houdt alle bijbehorende attributen bij, zodat de aanroepende code niet meerdere wijzigingen hoeft te coördineren.
Valideer eerst de hele wijziging
Voeg deze methoden toe binnen de klasse BankAccount met haar eigenaar, saldo en transactielijst:
def deposit(self, amount):
if amount <= 0:
raise ValueError("deposit must be positive")
self.balance += amount
self.transactions.append(("deposit", amount))
def withdraw(self, amount):
if amount <= 0:
raise ValueError("withdrawal must be positive")
if amount > self.balance:
raise ValueError("insufficient funds")
self.balance -= amount
self.transactions.append(("withdraw", amount))
Een storting wijzigt het saldo en legt de gebeurtenis vast. Een opname controleert zowel het teken van het bedrag als het beschikbare saldo voordat het bedrag wordt afgetrokken. Deze controles voorkomen dat een afgewezen opname het saldo of de transactiegeschiedenis wijzigt.
Als je begint met BankAccount("Maya", 50), geeft een storting van 25 het saldo 75 en een opname van 30 het saldo 45. Een latere poging om 100 op te nemen werpt ValueError op; het saldo blijft 45 en er wordt geen transactie toegevoegd. Als de aftrekking vóór de saldocontrole zou staan, zou de toestand ook bij een fout veranderen.
Maak onderscheid tussen een wijziging en een retourwaarde
Deze wijzigingsmethoden hebben geen return-instructie, dus geven ze None terug. Na new_balance = acct.deposit(25) heeft de storting plaatsgevonden, maar is new_balance gelijk aan None. Lees daarna acct.balance om het bijgewerkte getal te krijgen.
Een klasse kan ook een ander soort methode bieden die een nieuw object teruggeeft. resize suggereert dat een rechthoek wordt gewijzigd; resized kan worden gekozen voor een methode die een nieuwe rechthoek teruggeeft en het oorspronkelijke object ongemoeid laat. Lees het contract van de methode: de naam helpt, maar de definitie bepaalt het gedrag.
Volg gedeelde referenties
a = BankAccount("Maya", 50)
b = a
b.deposit(10)
print(a.balance) # 60
print(a is b) # True
Een toekenning bindt een naam aan een object; ze kopieert dat object niet. Beide namen verwijzen naar de rekening waarvan het saldo is gewijzigd. Als je a aan een functie doorgeeft, geef je ook toegang tot dat object door. Een functie die account.withdraw(5) aanroept, kan daardoor de rekening wijzigen die de aanroepende code ziet.
Maak voor een onafhankelijke rekening met dezelfde eigenaar en hetzelfde saldo BankAccount(a.owner, a.balance). Die constructor levert ook een nieuwe, lege transactiegeschiedenis. Het is een nieuwe rekening, geen volledige kopie van de geschiedenis.
copy.copy(a) maakt een nieuw buitenste object, maar behoudt referenties naar dezelfde binnenliggende objecten, waaronder de transactielijst. copy.deepcopy(a) kopieert ook geneste objecten. Als onafhankelijkheid van belang is, controleer dan zowel de objecten binnen de attributen als de buitenste instantie.
Een rechtstreekse toekenning acct.balance = -500 slaat de controles in de twee methoden over. Conventies voor interne attributen en properties helpen om duidelijk te maken hoe wijzigingen bedoeld zijn en om validatie dicht bij toestandswijzigingen te houden.
Belangrijkste punten
- Controleer een gevraagde wijziging voordat je attributen bijwerkt.
- Een wijzigingsmethode kan de toestand wijzigen en tegelijk
Noneteruggeven. - Bij toekenning en het doorgeven van argumenten blijven referenties naar hetzelfde object behouden.
- Een ondiepe kopie kan nog steeds geneste wijzigbare objecten delen.
- Houd samenhangende wijzigingen van de toestand bij elkaar in de methode die ervoor verantwoordelijk is.
3.3 Bescherm attributen met naamgevingsconventies en properties
Aanroepende code heeft een duidelijke manier nodig om een object te lezen en te wijzigen. Inkapseling houdt de regels voor de gegevens bij de methoden die ze beheren. Python gebruikt conventies en properties om die interface vorm te geven.
Markeer interne opslag
Een laag streepje aan het begin van _balance duidt een intern attribuut aan. Aanroepende code hoort de methoden van de rekening te gebruiken, maar Python staat rechtstreekse toegang nog steeds toe. Er is geen gereserveerd woord private dat de waarde afschermt.
Een naam zoals __balance, met twee lage streepjes aan het begin en geen dubbele lage streepjes aan het einde, leidt tot naamvervorming. Binnen class Acc wordt self.__balance opgeslagen onder _Acc__balance. Buiten de klasse mislukt obj.__balance, maar obj._Acc__balance kan nog steeds werken. Naamvervorming voorkomt botsingen tussen namen; ze beveiligt geen gevoelige gegevens.
Voer een methode uit via attribuutsyntaxis
Een property voert een methode uit wanneer een attribuut wordt gelezen. De regel @property is een decorator: die verandert hoe Python de methode beschikbaar maakt. Een property zonder setter staat lezen toe, maar wijst toekenning af.
class Account:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance
@property
def balance(self):
return self._balance
Lees na acct = Account("Maya", 50) de waarde van acct.balance zonder ronde haakjes. De toekenning acct.balance = 10 werpt AttributeError op omdat deze property geen setter heeft. Een stortingsmethode binnen de klasse zou _balance na haar controles kunnen bijwerken.
Controleer toekenning met een setter
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not 0 <= value <= 100:
raise ValueError("score must be between 0 and 100")
self._score = value
De getter en setter delen de naam score; de opgeslagen waarde gebruikt _score. Bij het maken van het object voert self.score = score de setter uit.
Voor s = Student("Ana", 88) slaat de toekenning s.score = 92 de waarde 92 op. De toekenning s.score = -1 werpt vóór het opslaan ValueError op, dus de score blijft 92. Student("Ana", 105) mislukt tijdens de initialisatie.
Binnen de setter zou self.score = value de setter opnieuw aanroepen, en dat herhaalt zich tot RecursionError. De getter loopt hetzelfde risico als die self.score teruggeeft. Gebruik de afzonderlijke opslagnaam _score in beide methode-inhouden.
Bereken waarden wanneer ze worden gelezen
Een property kan een waarde uit andere attributen berekenen:
@property
def area(self):
return self.width * self.height
Als deze property binnen een rechthoekklasse staat, kun je r.area gebruiken. Bij breedte 3 en hoogte 4 is de waarde 12. Als je de breedte wijzigt in 5, geeft de volgende leesactie 20 terug. Als je tijdens de initialisatie één keer een oppervlakte zou opslaan, blijft na een wijziging van de breedte een oude waarde staan.
Begin met een gewoon attribuut als eenvoudige opslag volstaat.
Belangrijkste punten
- Een laag streepje aan het begin markeert volgens conventie interne opslag; het blokkeert de toegang niet.
- Naamvervorming verandert een attribuutnaam om botsingen te voorkomen.
- Een property voert een getter uit via attribuutsyntaxis.
- Een setter kan initialisatie en latere toekenningen op één plek valideren.
- Gebruik een afzonderlijk opslagattribuut om te voorkomen dat de property zichzelf recursief aanroept.
- Een berekende property leidt haar waarde af wanneer ze wordt gelezen.
4Gebruik speciale methoden om objecten goed weer te geven en te vergelijken
Een bruikbaar object hoort vanzelfsprekend in gewone Pythonexpressies te passen. Zijn speciale methoden kunnen leesbare tekst, waardegelijkheid, een ordening, rekenresultaten of een lengte leveren. Leer welke methode Python in elke situatie opvraagt en wat die methode moet teruggeven. Let vooral op weergave binnen containers, stabiele dictionarysleutels en het verschil tussen een bewerking en de gespiegelde vorm ervan.
4.1 Geef objecten leesbare stringrepresentaties
Een object is gemakkelijker te onderzoeken als het zijn eigen gegevens kan beschrijven. Speciale methoden laten Python die beschrijving opvragen via bekende acties zoals print, str en repr. Hun namen beginnen en eindigen met twee lage streepjes en worden vaak dunder-namen genoemd.
Kies tekst voor twee doelgroepen
__str__ levert leesbare tekst voor gebruikers van een programma. __repr__ levert een nauwkeurige beschrijving voor programmeurs. Beide geven strings terug; geen van beide hoort het resultaat af te drukken in plaats van terug te geven.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(3, 4)
print(p) # (3, 4)
print(repr(p)) # Point(3, 4)
str(p) geeft de string "(3, 4)" terug. repr(p) geeft "Point(3, 4)" terug. Waar mogelijk lijkt een representatie op een klasseaanroep waarmee je de waarde opnieuw zou maken. Het blijft tekst: de aanroep van repr maakt zelf geen ander punt.
Zonder zelf gedefinieerde representatie toont een gewone instantie doorgaans een klassenaam en een identificatie die op een adres lijkt. Dat helpt om objecten uit elkaar te houden, maar toont hun bruikbare gegevens niet. Je roept deze speciale methoden zelden rechtstreeks aan; definieer ze één keer en gebruik de gewone weergavebewerkingen van Python.
Voorspel wat een container weergeeft
| Expressie | Opgevraagde representatie |
|---|---|
print(p) of str(p) | __str__ |
repr(p) | __repr__ |
f"{p}" | De gebruikelijke stringvorm voor deze klasse |
f"{p!r}" | __repr__ |
print([p]) | De weergave van de lijst, met repr voor het element |
Met de klasse hierboven toont print([Point(3, 4), Point(0, 0)]) [Point(3, 4), Point(0, 0)]. De lijst gebruikt de representaties van haar elementen, ook al heeft elk element ook __str__.
Als je alleen __repr__ definieert, gebruikt de standaardversie van __str__ die ook. Als je alleen __str__ definieert, verandert de weergave van repr of containers niet. Als je maar één weergavemethode toevoegt, is __repr__ daarom in meer situaties bruikbaar om objecten te onderzoeken.
Maak stringvelden ondubbelzinnig
Voor een boek dat title, author en year al opslaat, geven deze methoden verschillende bruikbare weergaven:
def __str__(self):
return f"{self.title} by {self.author} ({self.year})"
def __repr__(self):
return f"Book({self.title!r}, {self.author!r}, {self.year})"
Voor het boek met titel "Dune", auteur "Frank Herbert" en jaar 1965 geeft de eerste methode "Dune by Frank Herbert (1965)". De tweede geeft "Book('Dune', 'Frank Herbert', 1965)". !r vraagt de representaties van de stringvelden op, inclusief aanhalingstekens.
Een methode-inhoud met alleen print(self.x) geeft None terug, dus mislukt str(p) met TypeError. Ook een geheel getal teruggeven mislukt. Stel de vereiste tekst samen en geef die string terug.
Belangrijkste punten
__str__levert tekst voor gebruikers;__repr__levert een bruikbare representatie voor programmeurs.- Containers geven hun elementen weer met
repr. __repr__kan de standaardstringvorm leveren;__str__vervangtreprniet.- Beide methoden moeten strings teruggeven.
- Gebruik
!rin een f-string om representaties van stringvelden met aanhalingstekens te tonen.
4.2 Vergelijk en combineer objecten met operatoren
Speciale methoden verbinden een klasse met operatoren en ingebouwde functies. Definieer alleen bewerkingen die een duidelijke betekenis hebben voor dat type: vectoren optellen is begrijpelijk; een speler aan een team toevoegen is duidelijker als team.add_player(player).
Vergelijk waarden en houd hashes veilig
Twee afzonderlijke gewone instanties zijn standaard ongelijk bij een vergelijking. Voeg waardegelijkheid toe aan een puntklasse met:
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
Nu zijn twee punten op (3, 4) bij vergelijking gelijk, ook al verschilt hun identiteit. in, count, index en remove kunnen bij lijsten zulke gelijke waarden vinden. Het gebruikelijke resultaat van != volgt uit __eq__.
NotImplemented is een retourwaarde waarmee Python de andere operand of een terugvalmogelijkheid kan proberen. Het is geen exceptie die je moet opwerpen. Wanneer geen van beide operanden waardegelijkheid ondersteunt, valt == terug op identiteit; een punt en de string "3,4" zijn bij vergelijking ongelijk.
Als je __eq__ zonder __hash__ definieert, worden instanties niet-hashbaar. Ze kunnen dan geen elementen van een verzameling of sleutels in een dictionary zijn. Gelijke objecten moeten gelijke hashes hebben en de hash van een sleutel moet stabiel blijven. Als de vergeleken attributen nooit veranderen, kan een passende hashmethode hash((self.x, self.y)) teruggeven. Houd wijzigbare waardeobjecten niet-hashbaar wanneer hun gelijkheidsvelden kunnen veranderen.
Definieer een ordening
__lt__ levert <. Een studentklasse kan scores vergelijken met return self.score < other.score. De scores 88, 72 en 95 worden dan gesorteerd als 72, 88, 95. min kiest de laagste; max kiest de hoogste met > en de gespiegelde vergelijking met <.
Met alleen __lt__ kan a > b gebruikmaken van b < a. Python leidt <= of >= niet uit die definitie af; die hebben hun eigen methoden nodig. Als een volgorde alleen voor één taak nuttig is, geef je in plaats daarvan een sleutelfunctie door:
def by_score(student):
return student.score
ordered = sorted(students, key=by_score)
Geef nieuwe resultaten van rekenbewerkingen terug
Binnen een vectorklasse met de attributen x en y:
def __add__(self, other):
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, factor):
if not isinstance(factor, (int, float)):
return NotImplemented
return Vector(self.x * factor, self.y * factor)
def __rmul__(self, factor):
return self * factor
Vector(1, 2) + Vector(3, 4) levert een nieuwe vector (4, 6) op. Als je de eerste vector met 3 vermenigvuldigt, krijg je (3, 6) en blijft de oorspronkelijke vector ongewijzigd. __mul__ handelt de vector links af; __rmul__ ondersteunt een getal links. Python gaat er niet van uit dat de twee volgordes hetzelfde betekenen.
Lever een zinvolle lengte
Definieer voor een afspeellijst die self.songs opslaat __len__ om len(self.songs) terug te geven. Het resultaat moet een niet-negatief geheel getal zijn. len(playlist) telt dan de nummers. In deze klasse maakt lengte nul ook if playlist: onwaar; een zelf gedefinieerde __bool__ krijgt bij het toetsen van de waarheidswaarde voorrang op de lengte.
Belangrijkste punten
__eq__definieert waardegelijkheid; geefNotImplementedterug voor niet-ondersteunde operandtypen.- Gelijke hashbare objecten hebben gelijke, stabiele hashes nodig.
__lt__ondersteunt sorteren, maar levert geen<=of>=.- Gebruik een sleutelfunctie voor een sorteervolgorde die bij een specifieke taak hoort.
- Rekenkundige methoden kunnen nieuwe objecten maken zonder de operanden te wijzigen.
__rmul__ondersteunt vermenigvuldiging met het object rechts.__len__geeft een niet-negatief geheel getal terug en kan de waarheidsinterpretatie bepalen als__bool__niet bestaat.
5Bouw programma’s met samenwerkende objecten
Grotere programma’s verbinden objecten met verschillende taken. Een winkelwagen kan artikelen om subtotalen vragen; de ene rekening kan een andere vragen een storting te ontvangen. Volg de referenties en de volgorde van aanroepen, zodat je ziet welke toestand verandert en waar een fout de uitvoering stopt. Gebruik die ideeën daarna om een duidelijk ontwerp te kiezen, een kleine klasse te plannen en haar openbare gedrag in overzichtelijke stappen te testen.
5.1 Maak objecten die andere objecten bevatten
De attributen van een object kunnen naar andere objecten verwijzen. Een winkelwagen bevat artikelen, een bibliotheek bevat boeken en een cursus heeft cursisten. Compositie bouwt via deze heeft-een-relatie een groter object op uit kleinere objecten.
Geef elk object zijn eigen verantwoordelijkheid
class Item:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def subtotal(self):
return self.price * self.quantity
class ShoppingCart:
def __init__(self):
self.items = []
def add(self, item):
if not isinstance(item, Item):
raise TypeError("a cart holds Item objects")
self.items.append(item)
def total(self):
total = 0
for item in self.items:
total += item.subtotal()
return total
De winkelwagen heeft een afzonderlijke lijst die tijdens de initialisatie wordt gemaakt. De methode add controleert het binnenkomende object voordat ze het toevoegt. cart.add("pen") werpt TypeError op in plaats van tekst op te slaan die tot een fout leidt wanneer een subtotaal wordt opgevraagd.
Maak de artikelen vóór of tijdens de aanroepen:
cart = ShoppingCart()
cart.add(Item("notebook", 2.50, 4))
cart.add(Item("pen", 1.25, 3))
print(cart.total()) # 13.75
print(cart.items[0].name) # notebook
De subtotalen zijn 10.0 en 3.75; de winkelwagen telt ze op tot 13.75. De keten cart.items[0].name bereikt eerst de lijst van de winkelwagen, dan het eerste object daarin en daarna de naam van dat object.
Draag werk over aan het object dat het kent
De winkelwagen vraagt elk artikel om subtotal(). Dit is delegatie: één object roept voor een deel van een taak de methode van een ander object aan. Als een kwantumkorting verandert hoe een subtotaal wordt berekend, hoort die regel in Item.subtotal. De winkelwagen kan de teruggegeven subtotalen blijven optellen zonder prijsregels te herhalen.
Als je Item doorgeeft in plaats van Item(...), geef je de klasse door, geen gekocht artikel. De typecontrole wijst dat klasseobject af. Laat alle toevoegingen via de gecontroleerde route lopen; rechtstreekse toegang via cart.items.append(...) zou add omzeilen.
Zoek een object
Voeg deze methode toe binnen ShoppingCart:
def find(self, name):
for item in self.items:
if item.name == name:
return item
return None
Bij de eerste overeenkomst wordt het bestaande artikel teruggegeven. Zonder overeenkomst wordt None teruggegeven. Controleer found is None voordat je found.name gebruikt; None heeft dat attribuut niet. Een methode __len__ die len(self.items) teruggeeft, kan er ook voor zorgen dat len(cart) de opgeslagen artikelobjecten telt, terwijl __str__ artikelregels kan samenvoegen tot een bon.
Volg referenties binnen collecties
Een lijst slaat referenties op, dus als je hetzelfde artikel in twee winkelwagens plaatst, wordt het niet gekopieerd. Een wijziging van de prijs van dat artikel beïnvloedt het volgende totaal dat beide winkelwagens berekenen. Afzonderlijke winkelwagenlijsten voorkomen dat de lijst onbedoeld wordt gedeeld, maar maken de objecten erin niet onafhankelijk. Maak afzonderlijke artikelen wanneer de winkelwagens onafhankelijke gegevens moeten bevatten.
Belangrijkste punten
- Compositie slaat objecten op binnen een ander object.
- Bij delegatie vraag je een opgenomen object om zijn deel van een taak uit te voeren.
- Controleer een object voordat je het opslaat als latere methoden het type ervan nodig hebben.
- Een zoekactie kan het overeenkomende object of
Noneteruggeven. - Afzonderlijke containers kunnen nog steeds naar hetzelfde opgenomen object verwijzen.
5.2 Laat objecten samenwerken via methoden
Objecten kunnen net als getallen of strings argumenten en retourwaarden van methoden zijn. Volg het ontvangende object, de argumenten en het resultaat afzonderlijk: een methode kan een bestaand object wijzigen, een nieuw object teruggeven of alleen een waarde melden.
Gebruik een ander object als invoer
Binnen een puntklasse met x en y kan een methode beide punten lezen:
def midpoint(self, other):
return Point((self.x + other.x) / 2,
(self.y + other.y) / 2)
Voor p = Point(3, 4) en q = Point(1, 0) maakt middle = p.midpoint(q) een derde punt op (2.0, 2.0). Tijdens de aanroep verwijst self naar p en other naar q. De methode berekent coördinaten en maakt een nieuw punt; ze kent niets toe aan de oorspronkelijke punten.
Met import math kan een afstandsmethode math.hypot(self.x - other.x, self.y - other.y) teruggeven. Point(0, 0).distance_to(Point(3, 4)) geeft dan 5.0. Deze aanroep geeft een getal terug, terwijl midpoint een object teruggeeft.
Laat elk ontvangend object zijn wijzigingen beheren
Binnen een rekeningklasse met gecontroleerde methoden withdraw en deposit:
def transfer_to(self, other, amount):
self.withdraw(amount)
other.deposit(amount)
Als Maya 100 heeft en Leo 20, neemt maya.transfer_to(leo, 30) eerst geld op van Maya, zodat 70 overblijft, en stort het daarna bij Leo, die dan 50 heeft. In de eerste aanroep is self Maya’s rekening. In de stortingsaanroep is de eigen self Leo’s rekening.
Een latere overboeking van 500 mislukt in withdraw, omdat Maya nog maar 70 heeft. De uitvoering stopt vóór deposit, dus beide saldi behouden hun eerdere waarden. De volgorde van de aanroepen is van belang. Python maakt een eerdere toestandswijziging niet ongedaan als een latere aanroep een exceptie opwerpt; deze korte methode hangt ervan af dat de ontvangende rekening het bedrag kan accepteren.
De aanroep other.deposit(amount) voert de regels van de ontvangende rekening op één plek uit. Als je other.balance rechtstreeks zou wijzigen, zou je die regels overslaan.
Volg de objecten, niet alleen de variabelenamen
Teken elke instantie als een vak met haar attributen. Teken een pijl van elke variabele naar het bijbehorende vak. Bepaal bij een aanroep welk vak self ontvangt en welke vakken als argumenten worden doorgegeven. Werk het betreffende vak bij na elke geslaagde regel die de toestand wijzigt. Twee pijlen naar één vak betekenen dat een wijziging via beide namen hetzelfde object bereikt.
Een gewone functie kan ook met meerdere objecten werken. total_balance(accounts) kan rekeningen doorlopen en hun saldi optellen. Die berekening over de hele lijst hoeft niet bij één rekening te horen.
Geef een ontbrekende relatie weer
Een boek kan beginnen met self.borrower = None en later een lidobject opslaan. Controleer de relatie voordat je een attribuut van het lid leest:
if book.borrower is not None:
print(book.borrower.name)
Anders mislukt book.borrower.name wanneer het boek geen lener heeft. Klassen kunnen boven een functie main() worden gedefinieerd die de objecten maakt en verbindt. Door main() aan te roepen onder if __name__ == "__main__": voorkom je dat die acties worden uitgevoerd wanneer de klassen worden geïmporteerd.
Belangrijkste punten
- Herken het ontvangende object als
selfen het objectargument als zijn eigen parameter. - Een methode kan een nieuw object teruggeven terwijl de invoer ongewijzigd blijft.
- Aanroepen vinden op volgorde plaats; een exceptie stopt latere aanroepen, maar maakt eerdere wijzigingen niet ongedaan.
- Gebruik de openbare methoden van een ander object om zijn regels toe te passen.
- Controleer op
Nonevoordat je een optionele objectrelatie volgt. - Een functie kan berekeningen organiseren die meerdere objecten omvatten.
5.3 Bepaal wanneer een klasse een programma duidelijker maakt
Een klasse is op haar plaats wanneer samenhangende gegevens en herhaalde bewerkingen samen gemakkelijker te begrijpen zijn. Je hoeft niet elke functie of elk record in een klasse om te zetten. Kies de structuur die de verantwoordelijkheden van het programma duidelijk maakt.
Zoek gegevens en gedrag die samen voorkomen
Klassen helpen wanneer meerdere functies steeds dezelfde groep waarden ontvangen, veel records een structuur delen en toestandswijzigingen gemeenschappelijke regels moeten volgen. Meerdere rekeningen die allemaal stortingen en opnames ondersteunen, passen hier goed bij. Eén berekening kan een functie blijven. Een vast record dat alleen wordt gelezen, kan een tuple of dictionary blijven.
Een klasse met alleen inrichting en een eenmalige bewerking voegt vaak meer omslachtigheid dan nuttige structuur toe. Vraag je af wat één instantie voorstelt en welke terugkerende acties erbij horen. Als je die vragen niet eenvoudig kunt beantwoorden, heroverweeg dan het ontwerp.
Zet een bewerking bij haar gegevens
Een rekening met een dictionary kan zo beginnen:
account = {"owner": "Maya", "balance": 50}
def deposit(account, amount):
account["balance"] += amount
De klasseversie kan dezelfde bewerking bij haar toestand en regels plaatsen:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("deposit must be positive")
self.balance += amount
def __repr__(self):
return f"BankAccount({self.owner!r}, {self.balance})"
Na acct = BankAccount("Maya", 50) en acct.deposit(25) is de representatie BankAccount('Maya', 75). De regel dat stortingen positief moeten zijn, wordt nu uitgevoerd telkens wanneer aanroepende code die methode gebruikt. Het lezen van account["balence"] zou KeyError opwerpen; het lezen van acct.balence werpt AttributeError op. Een klasse neemt spelfouten niet weg, maar geeft de gegevens en hun bewerkingen een plek met een naam.
Ontwerp op basis van een korte beschrijving
Bij “Een bibliotheek leent boeken uit aan leden. Een lid kan maximaal 3 boeken lenen” suggereren de zelfstandige naamwoorden Book, Member en Library. De werkwoorden suggereren methoden voor uitlenen en terugbrengen.
- Noteer wat elke instantie weet: de titel, auteur en lener van een boek; de naam en geleende boeken van een lid; de boeken en leden van een bibliotheek.
- Bepaal de regels: een boek mag niet al zijn uitgeleend en een lid mag
MAX_LOANS = 3niet overschrijden. - Zet elke regel in de bewerking die de relatie wijzigt, zoals
Library.check_out(book, member). - Schrijf de initialisatie en een bruikbare representatie en voeg daarna steeds één actie toe en test die.
Verschillende titels hebben verschillende instanties van Book nodig, geen afzonderlijke klassen die naar elke titel zijn genoemd. Gebruik zelfstandige naamwoorden in het enkelvoud voor afzonderlijke objecten, zoals Book; noem een collectie naar de collectie, zoals Library. Houd de taak van elke klasse duidelijk genoeg om die in één zin te beschrijven.
Test waarneembaar gedrag
acct = BankAccount("Maya", 50)
acct.deposit(25)
assert acct.balance == 75
assert werpt AssertionError op als de voorwaarde onwaar is. Test ook afgewezen acties: vang de verwachte exceptie op en controleer dat de toestand niet is veranderd. Gebruik expliciete validatie met raise voor invoer van aanroepende code; asserties zijn controles tijdens de ontwikkeling en kunnen worden uitgeschakeld wanneer Python met optimalisatie wordt uitgevoerd.
Belangrijkste punten
- Kies een klasse wanneer samenhangende toestand en terugkerende bewerkingen baat hebben bij één gezamenlijke plek.
- Houd een eenvoudige berekening als functie wanneer geen blijvende objecttoestand nodig is.
- Ontwerp attributen op basis van de gegevens en methoden op basis van de acties en regels.
- Modelleer verschillende gegevenswaarden als instanties van één klasse.
- Test zowel geslaagde updates als afgewezen wijzigingen.
Klassen en objecten in Python: voorbeelden
1Van bekende waarden naar je eerste klasse
11 stappen
- 1IntroductieBegin met objecten die je kent
Een lijst, een string en een getal zijn allemaal objecten. Hun typen bepalen welke bewerkingen ze ondersteunen en elke waarde heeft haar eigen identiteit.
Begin met het volgen van een stringmethode en een lijstmethode. Gebruik daarna dezelfde ideeën om een eenvoudige klasse te maken waarvan de instanties verschillende gegevens opslaan. Blijf twee vragen stellen: welk object bereikt deze naam en wat geeft deze aanroep terug?
- 2VoorbeeldVraag het type van een waarde op
Python kan je vertellen wat voor soort object een waarde is.
text = "river" values = [4, 2] print(type(text)) # <class 'str'> print(isinstance(values, list)) # TrueDe string en de lijst bieden verschillende methoden omdat ze verschillende typen hebben. Je kunt
dir(values)gebruiken om de beschikbare namen op de lijst te bekijken, waaronder gewone en speciale methoden. - 3VoorbeeldVolg een teruggegeven string
Een stringmethode kan een nieuwe waarde opleveren zonder de oorspronkelijke waarde te vervangen.
name = "robin" result = name.title() print(result) # Robin print(name) # robintitle()geeft een nieuwe string terug. De toekenning slaat dat resultaat op onderresult, terwijlnamenog steeds naar"robin"verwijst. Als jename = name.title()zou schrijven, zounamenaar de teruggegeven string verwijzen. - 4VoorbeeldVolg een wijziging van een lijst ter plaatse
Vergelijk de stringaanroep met het sorteren van een lijst.
values = [4, 2] result = values.sort() print(values) # [2, 4] print(result) # NoneDe lijst zelf verandert en de retourwaarde is
None. Als je het resultaat van de aanroep opnieuw aanvalueszou toekennen, zou je de lijstreferentie van die naam vervangen doorNone. Gebruik alleenvalues.sort()wanneer je de bestaande lijst wilt wijzigen. - 5UitdagingWaar verschijnt de toevoeging?
Voorspel beide afgedrukte lijsten.
a = [7] b = a c = [7] b.append(2) print(a, c)---reveal---
De uitvoer is
[7, 2] [7].aenbverwijzen naar één lijst, dus toevoegen viabwijzigt de lijst die viaazichtbaar is.cbevat een afzonderlijke lijst. De lijsten waren aanvankelijk gelijk, maara is cwas onwaar. - 6TipTeken referenties voordat je wijzigingen volgt
Teken één vak voor elke afzonderlijk gemaakte lijst. Laat zowel
aalsbnaar het eerste vak wijzen en laatcnaar het tweede wijzen. Toevoegen wijzigt het vak; een toekenning verandert waar een naam naar wijst.Maak ook onderscheid tussen een methode en haar aanroep:
name.titlegeeft een methodeobject, terwijlname.title()het uitvoert. Ronde haakjes kunnen het verschil maken tussen gedrag vasthouden en het resultaat ervan verkrijgen. - 7AnalogieEén formulier, meerdere ingevulde exemplaren
Zie een klasse als een leeg formulier dat beschrijft welke informatie een bepaald soort object bevat. Een instantie is een ingevuld exemplaar met eigen gegevens.
Een klasse
Badgekan elke badge in een programma voorstellen. De ene instantie bevat de naam van een bezoeker; een andere bevat een andere naam. De klasse beschrijft het gemeenschappelijke soort object. Je hebt geen nieuwe klasse nodig voor elke waarde die op het formulier staat. - 8VoorbeeldDefinieer het type en maak twee instanties
Gebruik een minimale klasse terwijl je leert hoe instanties werken.
class Badge: pass first = Badge() second = Badge() first.name = "Rae" second.name = "Ivo"passlevert een lege inhoud. Elke klasseaanroep maakt een instantie en de toekenningen met een punt slaan gegevens op het gekozen object op.first is secondis onwaar. Metfirst = Badgezou je de klasse een andere naam geven zonder een instantie te maken. - 9UitdagingZoek een verkeerd gespeld attribuut
Ga verder met de twee badges:
first.nmae = "Tess" print(first.name, second.name)Wat verschijnt er?
---reveal---
De uitvoer is
Rae Ivo. De toekenning maakt een afzonderlijk attribuut met de naamnmae; ze wijzigtnameniet. Het lezen van een attribuut dat niet bestaat, werptAttributeErrorop, maar een waarde toekennen aan een nieuwe naam is toegestaan. Controleer bij een onverwachte waarde de spelling bij het toekennen en het lezen. - 10UitdagingCombineer instanties en aliassen
Ga verder met
firstenseconduit het badgevoorbeeld:copy_name = first copy_name.name = "Noor" print(first.name, second.name)---reveal---
De uitvoer is
Noor Ivo.copy_nameis een alias voor de eerste badge, dus de toekenning wijzigt dat object. De tweede badge blijft afzonderlijk. Door instanties te maken bepaal je hoeveel objecten er zijn; door namen toe te kennen bepaal je welke objecten die namen bereiken. - 11SamenvattingVolg nog een programma op papier
Probeer een kort programma met twee klasseaanroepen en een derde naam die aan de eerste instantie wordt toegekend. Geef de objecten verschillende attributen en wijzig er daarna één via de alias.
Teken voordat je het uitvoert de objectvakken en voorspel elke leesactie. Als een voorspelling afwijkt van de uitvoer, bekijk dan de spelling van de attributen en de pijlen van namen naar objecten. Zo maak je onderscheid tussen een fout bij de inrichting en een referentiefout.
2Maak objecten met betrouwbare begingegevens
14 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieRicht een takenverzameling in
- 2UitlegGeef Python een initialisatiemethode
- 3VoorbeeldKoppel een aanroep aan zijn parameters
- 4UitdagingHerken het ontbrekende attribuut
- 5VoorbeeldAccepteer een optionele beginlijst
- 6TipControleer hoever de kopie gaat
- 7UitlegVoeg gegevens over de klasse toe
- 8VoorbeeldVolg een toekenning die een waarde verhult
- 9UitdagingHerstel de gedeelde teller
- 10UitlegWijs een onbruikbaar maximum af
- 11VoorbeeldKies de fout en de herstelroute
- 12UitdagingWijst afdrukken de inrichting af?
- 13UitdagingBehoud het eerdere object bij een fout
- 14SamenvattingControleer de inrichting als aanroepende code
3Beheer toestand via methoden en properties
16 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieMaak een voorraadobject bruikbaar
- 2VoorbeeldBegin met een opgeslagen hoeveelheid
- 3UitlegLaat het ontvangende object self leveren
- 4UitdagingMaak een resultaat herbruikbaar
- 5VoorbeeldVoeg voorraad toe na controle van de hoeveelheid
- 6VoorbeeldWijs een verwijdering af vóór de aftrekking
- 7UitdagingVolg een wijziging via een alias
- 8VerbandKopieer de toestand op het juiste niveau
- 9UitlegMarkeer interne opslag
- 10VoorbeeldLees via een property
- 11VoorbeeldSta gecontroleerde openbare toekenning toe
- 12UitdagingVoorkom een recursieve setter
- 13KernpuntBereken een waarde wanneer die wordt gelezen
- 14UitdagingCombineer een setter met een alias
- 15TipLees de definitie en de aanroep samen
- 16SamenvattingTest steeds één bewerking
4Laat objecten in Pythonexpressies passen
17 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBepaal wat een object moet betekenen
- 2VoorbeeldBegin met een punt
- 3VoorbeeldVoeg tekst voor onderzoek toe
- 4UitdagingVoorspel de weergave van een lijst
- 5TipGeef in beide weergavemethoden tekst terug
- 6VoorbeeldVergelijk coördinaatwaarden
- 7KernpuntLaat niet-ondersteunde vergelijkingen aan Python over
- 8UitdagingKunnen gelijke paren in een verzameling?
- 9VoorbeeldKies een coördinatenordening
- 10VerbandEen volgorde kan bij één taak horen
- 11VoorbeeldTel op zonder de operanden te wijzigen
- 12VoorbeeldOndersteun beide volgordes van vermenigvuldiging
- 13UitdagingHoud een nieuw resultaat en een alias uit elkaar
- 14UitlegGebruik lengte voor een collectie
- 15UitdagingVoorspel de tak voor een lege wachtrij
- 16TipHoud de betekenis zichtbaar
- 17SamenvattingControleer elke expressie afzonderlijk
5Verbind objecten met duidelijke taken
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBouw een bestelling uit kleinere onderdelen
- 2VoorbeeldGeef één regel zijn eigen berekening
- 3VoorbeeldSla regels op in een bestelling
- 4VoorbeeldDraag elke kostenberekening over
- 5UitdagingVolg een regel die twee bestellingen delen
- 6TipControleer op een ontbrekend zoekresultaat
- 7UitlegGeef een object door aan een andere methode
- 8VoorbeeldVolg het ontvangende object en het resultaat
- 9UitdagingWaar stopt een mislukte combinatie?
- 10VerbandVolg aanroepen die twee objecten wijzigen
- 11UitlegBepaal welk gedrag bij elkaar hoort
- 12UitdagingVoorkom een klasse voor elk product
- 13VoorbeeldTest de gecontroleerde openbare methode
- 14UitdagingKies een test voor het gecombineerde ontwerp
- 15SamenvattingOntwerp zelf een kleine samenwerking
Klassen en objecten in Python: oefenvragen
Set 1: Samenwerkende objecten en duidelijke ontwerpen10 Oefenvragen
Vraag 1 van 10
Workshop slaat Tool-objecten op in self.tools. Wat leest workshop.tools[0].label?Toon antwoordVerberg antwoord
tools van de werkplaats, selecteert het eerste element met index 0 en leest de label van dat gereedschap. Ze geeft dus het label van één gereedschap terug. Ze verzamelt niet elk label; de index selecteert één object vóór de laatste attribuutleesactie.Vraag 2 van 10
line.cost() aan te roepen op elke opgeslagen regel. Een kwantumkorting verandert hoe de kosten van elke regel worden berekend. Waar hoort die regel?Toon antwoordVerberg antwoord
cost. De winkelmand kan blijven delegeren en de teruggegeven waarden optellen. Kortingslogica toevoegen aan de lus van de winkelmand zou de verantwoordelijkheid verdubbelen en andere aanroepen van cost andere regels laten gebruiken.Vraag 3 van 10
Welk totaal drukt de code af?
class Line:
def __init__(self, rate, count):
self.rate = rate
self.count = count
def cost(self):
return self.rate * self.count
x = Line(4, 3)
a = [x]
b = [x, Line(2, 5)]
a[0].count = 6
print(sum(line.cost() for line in b))
Toon antwoordVerberg antwoord
x, dus als je het aantal via a[0] wijzigt, worden de kosten 4 * 6, ofwel 24. De afzonderlijke regel in b kost 2 * 5, ofwel 10, samen 34. Het totaal 22 houdt het oude aantal aan en ziet de gedeelde referentie naar het element over het hoofd.Vraag 4 van 10
find(name) van een collectie geeft bij succes een object terug en bij een mislukte zoekactie None. Welke aanroepende code handelt beide gevallen af voordat result.name wordt gelezen?Toon antwoordVerberg antwoord
None voordat je de attributen ervan volgt. Als je eerst result.name leest, mislukt dat al wanneer er geen overeenkomst is gevonden. De waarheidswaarde van de naam controleren lost dat probleem niet op, omdat je nog steeds een werkelijk resultaatobject nodig hebt om de naam te bereiken.Vraag 5 van 10
Position.midpoint(other) geeft een nieuwe positie terug waarvan de coördinaten de gemiddelden zijn. Als a op (2, 8) ligt en b op (10, 4), wat is de situatie na c = a.midpoint(b)?Toon antwoordVerberg antwoord
(2 + 10) / 2 en (8 + 4) / 2, dus de nieuwe positie is (6, 6). De methode geeft een nieuw object terug en laat de oorspronkelijke objecten ongewijzigd. Coördinaten optellen zonder te delen zou (12, 12) opleveren, en dat is niet het midden.Vraag 6 van 10
send_to(other, n) roept self.remove(n) aan en daarna other.add(n). remove wijst een hoeveelheid boven de voorraad van de verzender af voordat iets wordt gewijzigd; add accepteert positieve hoeveelheden. De voorraad van de verzender begint op 9 en die van de ontvanger op 4. Een overdracht van 3 slaagt, daarna wordt een overdracht van 8 geprobeerd en de exceptie opgevangen. Welke voorraden blijven over?Toon antwoordVerberg antwoord
3 van 9 af en telt dat bij 4 op, zodat 6 en 7 overblijven. De tweede verwijdering wijst 8 af voordat de toestand verandert, dus de daaropvolgende toevoeging wordt niet uitgevoerd. Een voorraad van 15 bij de ontvanger gaat er ten onrechte van uit dat de uitvoering doorgaat naar add nadat de verwijdering een exceptie opwerpt.Vraag 7 van 10
add van de ontvanger aan. De ontvanger wijst de hoeveelheid af voordat zijn eigen voorraad verandert. Er is geen code om wijzigingen terug te draaien en de aanroepende code vangt de exceptie op. Welke toestand blijft over?Toon antwoordVerberg antwoord
Vraag 8 van 10
Toon antwoordVerberg antwoord
Vraag 9 van 10
Toon antwoordVerberg antwoord
Bike kan attributen en gecontroleerde methoden voor verhuren en terugbrengen definiëren. Maak één instantie per fiets. Een klasse per label zou dezelfde actieregels herhalen, terwijl aliassen van één object één beschikbaarheidstoestand zouden delen.Vraag 10 van 10
De plank moet een derde boek afwijzen en de twee bestaande boeken behouden. Welke assertie brengt na de hieronder opgevangen fout de onjuiste implementatie aan het licht?
class Shelf:
def __init__(self):
self.limit = 2
self.books = []
def add(self, title):
self.books.append(title)
if len(self.books) > self.limit:
raise ValueError("shelf full")
shelf = Shelf()
shelf.add("Atlas")
shelf.add("Birch")
try:
shelf.add("Cove")
except ValueError:
pass
Toon antwoordVerberg antwoord
Set 2: Pas de ideeën toe in nieuwe situaties10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
isinstance(items, list) terug voor een ingebouwde lijst items?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Wat drukt deze code af?
class Tray:
labels = []
a = Tray()
b = Tray()
a.labels.append("red")
b.labels = ["blue"]
Tray.labels.append("green")
print(a.labels, b.labels)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Wat drukt deze code af?
class Kit:
def __init__(self, parts=None):
self.parts = [] if parts is None else list(parts)
source = [[1]]
k = Kit(source)
source.append([2])
source[0].append(3)
print(k.parts)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
_reading wordt opgeslagen. De initialisatiemethode kent rechtstreeks toe aan self._reading, dus Meter(-2) slaagt. Door welke wijziging gebruikt het maken dezelfde controle als latere openbare toekenningen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Key vergelijkt gelijkheid alleen op basis van name, en zijn hash gebruikt ook name. Twee verschillende instanties beginnen met de naam "north"; één wordt een dictionarysleutel. Waarom moet de naam van die sleutel stabiel blijven?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
members, maar beide lijsten bevatten dezelfde instantie van Member. Eén club wijzigt de naam van dat lid. De clubs hebben nu onafhankelijke lidnamen nodig. Welke wijziging is nodig?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
__str__, maar een afgedrukte lijst met berichten toont nog steeds standaardobjectbeschrijvingen. Welke toevoeging verbetert die lijstelementen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
a.combine(b) hoort beide invoerobjecten ongewijzigd te laten. De inhoud roept self.parts.extend(other.parts) aan en geeft daarna self terug. Welk gedrag heeft die implementatie daadwerkelijk?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
class Vault kent de inrichting self.__code = "x" toe. Wat verklaart waarom vault.__code mislukt terwijl vault._Vault__code kan werken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
take van een klasse gebruikt assert n > 0 als enige controle op invoer van de aanroepende code. De regel moet ook gelden wanneer Python met optimalisatie is ingeschakeld. Welke wijziging is nodig?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Methoden, toestandswijzigingen en properties10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
self wanneer het programma panel.area() aanroept?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Door welke wijziging kan de aanroepende code double berekenen?
class Pace:
def __init__(self, minutes):
self.minutes = minutes
def seconds(self):
print(self.minutes * 60)
p = Pace(3)
double = p.seconds() * 2
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
"12,14" doorgeeft?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Wat drukt deze code af?
class Tank:
def __init__(self):
self.level = 5
def add(self, amount):
self.level += amount
a = Tank()
b = a
result = b.add(4)
print(a.level, result)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Wat drukt deze code af?
import copy
class Log:
def __init__(self):
self.entries = []
a = Log()
b = copy.copy(a)
b.entries.append("open")
b.entries = ["closed"]
print(a.entries, b.entries, a is b)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
name?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Waarom roept een toekenning aan reading.value deze setter herhaaldelijk aan?
class Reading:
@property
def value(self):
return self._value
@value.setter
def value(self, number):
self.value = number
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
remove(n) moet niet-positieve hoeveelheden en verwijderingen groter dan de huidige waarde afwijzen. Welke volgorde laat de toestand bij beide afwijzingen ongewijzigd?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Wat drukt deze code af?
class Dial:
def __init__(self, value):
self.value = value
@property
def value(self):
return self._value
@value.setter
def value(self, number):
if not 0 <= number <= 9:
raise ValueError("out of range")
self._value = number
d = Dial(4)
try:
d.value = 12
except ValueError:
pass
print(d.value)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
area met een alleen-lezenproperty uit width en height. Een object heeft breedte 6 en hoogte 2; daarna wordt aan de breedte 8 toegekend. Wat geeft het lezen van obj.area terug?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Leesbare weergaven en bruikbare operatoren10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
__str__ als __repr__. Welke methode levert de tekst van een instantie wanneer die in een afgedrukte lijst verschijnt?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Wat drukt deze code af?
class Tag:
def __init__(self, text):
self.text = text
def __repr__(self):
return f"Tag({self.text!r})"
t = Tag("ok")
print(t)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
str(marker) TypeError op wanneer de methode __str__ alleen print(self.code) aanroept?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Label definieert gelijkheid op basis van haar tekst en definieert geen __hash__. Twee labels bevatten dezelfde tekst. Wat gebeurt er als ze in een verzameling worden geplaatst?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Mark definieert alleen __lt__, die numerieke scores vergelijkt, en geen andere eigen vergelijkingen. Wat gebeurt er voor a met score 2 en b met score 7?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Vector definieert __mul__ voor vector * number, maar geen methode voor gespiegelde vermenigvuldiging. Welke methode moet worden toegevoegd om ook number * vector te ondersteunen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
__eq__ met code en __hash__ als hash(self.code). Haar objecten worden gebruikt als dictionarysleutels. Welke ontwerpwijziging maakt het sleutelgedrag betrouwbaar?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Queue definieert __len__ als het aantal wachtende taken en definieert geen __bool__. Wat zijn len(q) en bool(q) als er geen wachtende taken zijn?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Wat drukt de laatste regel af?
class Offset:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Offset(self.value + other.value)
a = Offset(6)
b = a
a = a + Offset(4)
print(a.value, b.value, a is b)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Rank.__lt__ ordent records op prioriteit. Eén rapport moet die records in plaats daarvan op aanmaaktijd sorteren zonder hun gebruikelijke volgorde te veranderen. Welke aanpak past daarbij?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 5: Objecten, referenties en betrouwbare inrichting10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Wat drukt deze code af?
a = [6, 2]
b = a
a = sorted(a)
b.append(9)
print(a, b)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Wat drukt deze code af?
label = "cedar"
label.upper()
print(label)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Wat bevat first.name aan het einde?
class Badge:
pass
first = Badge()
second = Badge()
first.name = "Rae"
second.name = "Ivo"
first.nmae = "Sol"
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Wat drukt deze code af?
class Counter:
def __init__(self, start=4):
self.value = start
c = Counter(9)
print(c.value)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Waarom mislukt de laatste regel?
class Lamp:
def __init__(self, brightness):
brightness = brightness
lamp = Lamp(7)
print(lamp.brightness)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Wat drukt deze code af?
class Folder:
def __init__(self, tags=[]):
self.tags = tags
a = Folder()
b = Folder()
a.tags.append("urgent")
a.tags = []
print(b.tags, a.tags)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Wat drukt deze code af?
class Ticket:
fee = 3
x = Ticket()
y = Ticket()
x.fee = 8
Ticket.fee = 5
print(x.fee, y.fee)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Wat drukt deze code af?
class Job:
count = 0
def __init__(self):
self.count += 1
a = Job()
Job.count = 10
b = Job()
print(a.count, b.count, Job.count)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Wat drukt deze code af?
class Batch:
def __init__(self, size):
if size <= 0:
raise ValueError("size must be positive")
self.size = size
batch = Batch(6)
try:
batch = Batch(0)
except ValueError:
pass
print(batch.size)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
alias
backup = entries wijzigt toevoegen via backup ook de lijst die via entries wordt bereikt.assertie
AssertionError opwerpt als haar voorwaarde onwaar is; Python kan die bij uitvoering met optimalisatie weglaten.assert acct.balance == 75 het gedrag; afgewezen invoer van aanroepende code heeft nog steeds expliciete validatie nodig.attribuut
book.title = "Emma" slaat een titel op, die je daarna kunt lezen door book.title te evalueren.attribuutopzoeking
p geen eigen limit heeft, kan het lezen van p.limit de gedeelde waarde limit vinden die op de klasse is gedefinieerd.C
compositie
container
cart.items[0] bereik je het eerste opgeslagen artikel.D
delegatie
diepe kopie
copy.deepcopy(account) ook die lijst gekopieerd, zodat toevoegen aan de geschiedenis van de kopie het origineel ongemoeid laat.G
gebonden methode
r.area levert een gebonden methode; als je ronde haakjes toevoegt zoals in r.area(), wordt die uitgevoerd en de berekende oppervlakte teruggegeven.gedragstest
gespiegelde bewerking
3 * vector kan Python de __rmul__ van de vector gebruiken wanneer vermenigvuldiging van gehele getallen die vector niet kan verwerken.H
hashbaarheid
__eq__ zonder __hash__ definieert, wordt niet-hashbaar, ook als haar instanties in een lijst werken.I
identiteit
is toetst of twee referenties hetzelfde object bereiken.a == b terwijl a is b onwaar is, omdat hun identiteit verschilt.initialisatie
inkapseling
instantie
Book() en Book() maken twee afzonderlijke boekinstanties, ook al hebben beide hetzelfde type.instantieattribuut
size en een afzonderlijke lijst met beleg opslaan via toekenningen op self.instantiemethode
self.r.area() levert Python r aan de methode, zodat die de breedte en hoogte van die rechthoek kan vermenigvuldigen.intern attribuut
balance, terwijl de eigen methoden van de klasse de opgeslagen _balance beheren.invariant
0 en 100 blijft.K
klasse
Book kan veel boeken maken, die elk een andere titel opslaan en dezelfde methodedefinities delen.klasseattribuut
MAX_TOPPINGS voor de hele klasse kan de standaardgrens leveren die via veel verschillende pizzaobjecten wordt gelezen.klassenontwerp
M
methode
"cedar".upper() voert een stringmethode uit, terwijl scores.append(7) een methode op een lijst uitvoert.N
naamverhulling
p.limit = 8 vervangt een wijziging van Pizza.limit niet de afzonderlijke grens die al op p is opgeslagen.naamvervorming
Acc wordt self.__balance _Acc__balance, dat toegankelijk blijft onder de vervormde naam.None
None is de unieke Pythonwaarde voor een ontbrekend of niet-beschikbaar resultaat en is ook bruikbaar voor een relatie waaraan nog geen object is toegekend.borrower = None opslaan totdat een lid het leent, zodat code controleert voordat ze de naam van een lener leest.NotImplemented
NotImplemented is een speciale retourwaarde die aangeeft dat een vergelijkings- of numerieke methode de opgegeven operanden niet ondersteunt.NotImplemented teruggeven voor een string, zodat Python het niet-ondersteunde paar afhandelt.O
object
"hello" en de lijst [2, 5] zijn beide objecten, al bieden hun typen verschillende bewerkingen.objectargument
other in p.midpoint(q) verwijst naar q, waarvan de methode de coördinaten leest om haar resultaat te berekenen.ondiepe kopie
copy.copy(account) is gemaakt die lijst nog steeds delen met het origineel.ontvangend object
self-argument van de methode wordt doorgegeven.sender.transfer_to(receiver, 5) ontvangt sender de overdrachtsaanroep; het rekeningargument is de bestemming.operatoroverloading
__add__ van een vector kan a + b een nieuwe vector laten opleveren door de overeenkomstige coördinaten op te tellen.opslagattribuut
score leest _score; als je binnen de eigen setter aan score zou toekennen, zou die setter herhaaldelijk worden aangeroepen.P
property
student.score kan een getter aanroepen en de toekenning student.score = 90 kan een validerende setter aanroepen.puntnotatie
cart.items geeft de naam vóór de punt het object aan en selecteert de naam erna het attribuut met artikelen.R
referentie
representatie voor programmeurs
__repr__ teruggeeft en die wordt gebruikt bij onderzoek en in containerweergaven."Book('Emma', 'Jane Austen', 1815)" tonen, inclusief tekstvelden tussen aanhalingstekens.retourwaarde
r1.area() + r2.area() resultaten optellen, terwijl alleen afdrukken None als elk resultaat overlaat.S
self
self ontvangt volgens conventie de huidige instantie, zodat een methode de attributen van dat object kan lezen of wijzigen.self.owner = owner slaat de linkerkant gegevens op de instantie op en leest de rechterkant een lokale parameter.setter
score die 105 afwijst voordat aan _score wordt toegekend, laat een eerdere geldige score ongewijzigd.sleutelfunctie
sorted(records, key=by_date) aanroepen zonder de gebruikelijke vergelijkingsmethode van de recordklasse te veranderen.speciale methode
__str__ op bij het omzetten van een object naar zijn leesbare stringvorm en __len__ voor zijn lengte.standaardargument
balance=0 in de initialisatiemethode begint Account("Rae") met nul zonder dat een saldoargument wordt doorgegeven.stringrepresentatie
__str__ teruggeeft en die normaal gesproken bedoeld is voor gebruikers van het programma."(3, 4)", terwijl zijn numerieke coördinaatattributen ongewijzigd blijven.T
terugval op de representatie voor programmeurs
__repr__ gebruiken wanneer een klasse geen eigen __str__ definieert.__repr__ toevoegen kan zowel print(point) als de weergave van het punt binnen een afgedrukte lijst verbeteren.toestand
type
type([2, 5]) geeft de klasse list terug en isinstance([2, 5], list) bevestigt dat de waarde een lijst is.TypeError
TypeError meldt dat een bewerking een ongeschikt type heeft ontvangen of is aangeroepen met argumenten die haar definitie niet accepteert.TypeError op, omdat die vergelijking tekst en nul niet kan ordenen.U
uitleesmethode
area() van een rechthoek leest de breedte en hoogte en geeft een getal terug zonder de afmetingen te wijzigen.V
validatie
ValueError
ValueError meldt dat een argument een aanvaardbaar type heeft, maar dat de waarde buiten wat de bewerking accepteert valt.-2 kan ValueError opwerpen met de melding "width must be positive".verantwoordelijkheid
voorwaarde voor directe uitvoering
main() onder if __name__ == "__main__": zodat een ander programma de klassen kan importeren zonder het programma te starten.W
waardegelijkheid
__eq__, zonder dezelfde objectidentiteit te vereisen.waarheidsinterpretatie
__bool__ maakt een gedefinieerde lengte nul het object onwaar.__len__ en zonder eigen __bool__ zorgt ervoor dat if playlist: zijn inhoud overslaat.wijzigbaarheid
upper() op een string toepast, krijg je een nieuwe string terug.wijzigbare standaardwaarde
items=[] rechtstreeks wordt opgeslagen, kunnen twee objecten die zonder dat argument zijn gemaakt naar dezelfde lijst verwijzen.wijzigingsmethode
Z
zoekresultaat
None, die aangeeft dat geen overeenkomst is gevonden.found is not None voordat ze found.name leest uit een zoekactie naar een artikel in een winkelwagen.#
__init__
__init__ initialiseert een instantie nadat Python die heeft gemaakt en moet None teruggeven wanneer de initialisatie is voltooid.Counter(5) geeft de nieuwe instantie en 5 door aan de initialisatiemethode voordat de ingerichte teller wordt teruggegeven.__lt__
__lt__ implementeert de vergelijking kleiner dan en levert een ordening die bewerkingen zoals sorted kunnen gebruiken.__lt__ op basis van scores laat een lijst met studentobjecten sorteren van de laagste naar de hoogste score.Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.