0%

Objecten weergeven en vergelijken · oefening

Identiteit en gelijkheid verschillen

Twee vragen met precies dezelfde vraagtekst, hetzelfde antwoord en dezelfde punten. Zijn het dezelfde vraag?

Try it

False. Twee objecten die identiek lijken, door hun eigen repr identiek worden afgedrukt, en Python zegt dat ze niet gelijk zijn.

Wat == standaard doet

Zonder andere instructies stelt == op je eigen objecten een veel beperktere vraag dan je waarschijnlijk bedoelde. Het vraagt: zijn dit hetzelfde object?

Try it

first is second is False, omdat er twee afzonderlijke objecten zijn aangemaakt. third is first is True, omdat third = first een verwijzing bewaarde naar het object dat al bestond. Dit is het idee van twee namen voor één object uit hoofdstuk 4. Standaard geeft == precies hetzelfde antwoord als is.

Hier zitten twee verschillende ideeën achter, en Python houdt ze gescheiden. Het diagram noemt dit ‘Matching contents, different objects’: overeenkomende inhoud, verschillende objecten. ‘Question object A’ en ‘Question object B’ zijn twee afzonderlijke vraagobjecten; ‘default’ bij de vergelijking betekent dat het om het standaardgedrag gaat.

  • Identiteit vraagt of twee namen naar één object in het geheugen verwijzen. is beantwoordt die vraag en je kunt dit nooit zelf definiëren.

  • Gelijkheid vraagt of twee objecten als dezelfde waarde moeten gelden. == beantwoordt die vraag en die beslissing hoort bij de klasse.

Twee afzonderlijk aangemaakte Question-objecten hebben een gelijke vraagtekst, een gelijk antwoord en gelijke punten, maar verschillende identiteiten; een alias wijst naar het eerste object en deelt de identiteit daarvan.

In dit hoofdstuk willen we dubbele vraaginhoud herkennen. Voor die taak moeten vragen met overeenkomende vraagteksten, antwoorden en punten als gelijk worden vergeleken. Een ander quizsysteem zou elke opgestelde vraag een eigen identiteit kunnen geven en ze bewust afzonderlijk kunnen houden.

a = Question("Q", "A", 1) en b = a, daarna c = Question("Q", "A", 1). Wat is waar zonder een gedefinieerde __eq__?

Waar de standaard tegenwerkt

De standaard is niet alleen verrassend. Die maakt ongemerkt dingen stuk die je al hebt geschreven:

Try it

in meldt False en herkent een nieuw aangemaakte vraag dus niet als inhoudelijk duplicaat. Methoden zoals .remove() zouden in plaats daarvan ValueError opgooien als ze geen overeenkomst vinden. in, .index(), .remove(), .count() en list1 == list2 zijn allemaal gebouwd op == en nemen dus allemaal over wat de klasse heeft besloten.

Wanneer de standaard juist is

Voordat je die haastig verandert, bedenk dan dat de standaard vaker juist is dan het lijkt.

Voor een QuizAttempt zou gelijkheid op basis van inhoud verkeerd zijn. Twee cursisten die op dinsdag allebei 4 van de 10 punten haalden, deden niet dezelfde poging. Als de ene later wordt gecorrigeerd, mag de andere niet mee veranderen. Een poging is een gebeurtenis: die heeft een eigen identiteit en twee pogingen zijn alleen dezelfde als ze letterlijk één en dezelfde zijn.

Het onderscheid dat de doorslag geeft:

SoortGelijk wanneerVoorbeelden
WaardeobjectDe inhoud overeenkomtQuestion, een punt, een geldbedrag, een datum
EntiteitZe één en dezelfde zijnQuizAttempt, een cursist, een bestelling, een sessie

Waardegelijkheid behandelt overeenkomende inhoud als uitwisselbaar bij vergelijkingen. Het zorgt er niet voor dat twee objecten latere wijzigingen delen. In de volgende oefening gebruiken we alleen-lezen vraagtoestand om dit waardemodel makkelijker te bewaren. Entiteiten zijn niet uitwisselbaar: attempt_a en attempt_b zijn twee dingen die zijn gebeurd, hoe sterk ze ook op elkaar lijken.

De volgende les definieert gelijkheid voor de waardeobjecten. Deze les zorgt ervoor dat je het onderscheid kunt maken, omdat waardegelijkheid op een entiteit gebeurtenissen ongemerkt kan samenvoegen die afzonderlijk moeten blijven.

Opdracht

Ontdek wat het huidige gedrag werkelijk is voordat je verandert hoe iets wordt vergeleken.

Maak de drie functies af met alleen wat Python nu al doet. Geen van deze klassen definieert al __eq__.

  • same_object(a, b) meldt of twee namen naar één object verwijzen.

  • default_equal(a, b) meldt wat == er nu over zegt.

  • found_in(bank, wanted) meldt met in of er een vraag gelijk aan wanted in de lijst staat.

Het onderscheid tussen waardeobjecten en entiteiten is een ontwerpbeslissing, geen truc om strings aan elkaar te koppelen, en blijft daarom in de uitgewerkte bespreking. De oefening richt zich op het waarnemen van de drie Pythonbewerkingen die het onderscheid zichtbaar maken.