Python heeft vanaf de eerste versies het mogelijk gemaakt om objecten met elkaar te vergelijken. Om te beschrijven wat "groter" of "gelijk aan" betekent voor jouw objecten, zijn er speciale vergelijkingsmethoden (eq, ne, lt, le, gt, ge) gecreëerd. Met de komst van PEP 207 (Python 2.1+) is er een uniform vergelijkingsprotocol geïmplementeerd, zodat programmeurs het gedrag bij vergelijkingen expliciet konden controleren.
Standaardtypen (int, str) worden "op waarde" vergeleken, maar door de identiteit (is) vergeleken voor gebruikersklassen, tenzij anders ingesteld. Als de magische vergelijkingsmethoden niet worden geïmplementeerd of onjuist worden geïmplementeerd, kan de vergelijking van jouw objecten leiden tot onverwacht gedrag bij het werken met collecties, sortering, en het gebruik van set en dict, en soms zelfs tot typefouten.
Het is noodzakelijk om de vergelijkingsmethoden expliciet in jouw klasse te definiëren. Vaak worden eq (gelijkheid) en lt (minder dan) geïmplementeerd, en de andere methoden kunnen automatisch worden verkregen met behulp van de decorator functools.total_ordering.
Voorbeeldcode:
from functools import total_ordering @total_ordering class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented return (self.x, self.y) < (other.x, other.y) # Gebruik: p1 = Point(1, 2) p2 = Point(1, 3) print(p1 < p2) # True print(p1 == p2) # False
Belangrijke kenmerken:
Is het mogelijk om alleen één vergelijkingsmethode (eq of lt) te implementeren en zal alles werken?
Nee. Door alleen eq te implementeren zorg je voor een correcte gelijkheid vergelijking, maar de operators <, >, <=, >= zullen zich standaard gedragen (meestal door het vergelijken van de objectidentifiers). Voor volledig gedrag moet je een voldoende set methoden implementeren of gebruik maken van functools.total_ordering.
Waarom bestaat compare(x, y) niet in Python, zoals in andere talen?
Python erfde het idee van speciale "magische" methoden voor elke operator en de functie cmp werd verwijderd in Python 3. Voor algemene vergelijkingen van objecten worden rich comparison methoden (lt, eq enz.) gebruikt, en voor sorteringen de key parameter in de sort/sorted functies.
Wat gebeurt er als je een object vergelijkt met een type waarvoor de vereiste methode niet is geïmplementeerd?
Als je in eq of andere methoden NotImplemented retourneert bij niet-ondersteunde types, zal Python automatisch de omgekeerde methode op de tweede operand proberen of False retourneren. Als je deze situatie niet behandelt, kan dit leiden tot een TypeError of volledig onjuist gedrag.
Voorbeeldcode:
class Foo: def __eq__(self, other): return NotImplemented class Bar: pass print(Foo() == Bar()) # False (probeer via Bar.__eq__)
Een ontwikkelaar heeft een User klasse gemaakt en alleen eq geïmplementeerd zonder hash, zodat gebruikers als gelijk worden beschouwd op basis van hun email. Vervolgens probeerde hij User objecten in een set te gebruiken of als sleutels van een dictionary.
Voordelen:
Nadelen:
Een programmeur gebruikt @total_ordering en implementeert eq en lt met typecontrole, en voegt hash toe als de klasse onveranderlijk is. Objecten worden correct vergeleken, gesorteerd en werken als sleutels van dictionaries.
Voordelen:
Nadelen: