Intuitiv ist uns klar, was es bedeutet, dass zwei Objekte gleich sind. Zum Beispiel wissen wir, dass wenn
s1 = "Hallo"
s2 = "Hallo"
dann sollte auch s1 und s2 gleich sein. Also
s1 == s2
True
Für die von Python (oder Java) vordefinierten Typen wie Strings, Int, Float, ... stimmt dies auch tatsächlich. Wenn wir aber unsere eigenen Typen definieren, wird die Sache etwas schwieriger. Wir veranschaulichen dies am Beispiel der Klasse Datum, die wie folgt definiert ist:
class Date:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
Jetzt beobachten wir, was passiert, wenn wir zwei Objekte vom Typ Datum vergleichen.
d1 = Date(1, 1, 2018)
d2 = Date(1, 1, 2018)
d1 == d2
False
Wir sehen, dass diese Objekte von Python nicht als gleich erachtet werden. Der Grund ist, dass per default die Gleichheit nur auf Objektidentitäten definiert ist, d.h es wird nur verglichen ob es das gleich Objekt ist, und nicht ob es denselben Inhalt hat.
Da es sich um zwei verschiedene Objekte handelt, haben diese auch unterschiedliche IDs, wie wir mithilfe der Python Funktion id leicht herausfinden können
print("id d1: ", id(d1))
print("id d2: ", id(d2))
id d1: 2027592443552 id d2: 2027592442488
Die Identität ist nur gleich, wenn es sich wirklich um zweimal dasselbe Objekt handelt, also
d3 = d1
print("id d1: ", id(d1))
print("id d3: ", id(d3))
print("d1 == d3:", d1 == d3)
id d1: 2027592443552 id d3: 2027592443552 d1 == d3: True
Damit wir auch für benutzerdefinierte Typen, richtige Objektgleichheit haben, müssen wir die Methode __eq__
implementieren (in Java entspricht dies der Methode equals())
class Date:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
def __eq__(self, rhs):
return self.day == rhs.day \
and self.month == rhs.month \
and self.year == rhs.year
Wenn wir jetzt nochmals zwei Objekte definieren, sollte alles richtig funktionieren.
d1 = Date(1, 1, 2018)
d2 = Date(1, 1, 2018)
d3 = Date (2, 2, 2018)
print("d1==d2: ", d1 == d2)
print("d1==d3: ", d1 == d3)
print("d1!=d3: ", d1 != d3)
d1==d2: True d1==d3: False d1!=d3: True
Die Implementation von __eq__
sollte folgende Anforderungen erfüllen:
Mathematisch ausgedrückt: __eq__
ist eine Äquivalenzrelation.
Manchmal wollen wir nicht nur feststellen können, ob zwei Objekte gleich sind, sondern diese auch Ordnen können. Wie bei Gleichheit von Objekten kümmert sich Python / Java um die Ordnung von den vordefinierten Typen.
"AA" < "AB"
True
"AB" > "AA"
True
Für Benutzerdefinierte Typen, die ja nicht immer eine Ordnung haben, sind diese Operationen nicht definiert.
d1 = Date(1, 1, 1970)
d2 = Date(1, 1, 2018)
d1 > d2
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-52-cc0a9146f3fe> in <module>() ----> 1 d1 > d2 TypeError: unorderable types: Date() > Date()
Um diese Operation zu unterstützen, müssen wir die Python Methode __lt__
implementieren und die Klasse mittels einer Annotation als geordnet kennzeichnen.
(Alternativ können wir auch alle funktionen __lt__
, __le__
, __gt__
und __ge__
implementieren)
from functools import total_ordering
@total_ordering
class Date:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year
def __eq__(self, rhs):
return self.day == rhs.day \
and self.month == rhs.month \
and self.year == rhs.year
def __lt__(self, rhs):
return self.year < rhs.year \
or (self.year == rhs.year and self.month < rhs.month) \
or (self.year == rhs.year and self.month == rhs.month) \
and self.day < rhs.day
d1 = Date(1, 1, 1970)
d2 = Date(1, 1, 2018)
d1 < d2
True
d1 <= d1
True