6.1 Magische Methoden
Die Überladung von Operatoren in Python ermöglicht es, das Verhalten von eingebauten Operatoren (wie z.B. +
, -
, *
, /
) für benutzerdefinierte Klassen zu definieren oder zu ändern. Dies geschieht mithilfe von speziellen Methoden, die auch als magische Methoden bezeichnet werden.
Zum Beispiel kannst du in deiner Klasse Vergleichsoperatoren überladen:
Operator | Methode ohne Unterstrich | Methodensignatur |
---|---|---|
== | eq() |
__eq__(self, other) |
!= | ne() |
__ne__(self, other) |
< | lt() |
__lt__(self, other) |
<= | le() |
__le__(self, other) |
> | gt() |
__gt__(self, other) |
>= | ge() |
__ge__(self, other) |
Angenommen, du hast deine eigene Klasse geschrieben und möchtest, dass die Objekte deiner Klasse genauso verglichen werden, wie du es möchtest. Du musst einfach in deiner Klasse die Methode „__eq__“
implementieren, und Python wird sie jedes Mal aufrufen, wenn Objekte deiner Klasse im Code verglichen werden.
Beispiel:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Verwendung
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2) # Gibt aus: True
print(v1 == v3) # Gibt aus: False
Jedes Mal, wenn du zwei deiner Objekte vergleichst, prüft Python, ob sie eine implementierte
Funktion „__eq__“
haben. Falls ja, wird diese aufgerufen. Wenn nicht, werden einfach die Referenzen der Objekte verglichen.
Im obigen Beispiel ist faktisch geschrieben (nur sollte noch eine Prüfung auf das Vorhandensein der Methode hinzugefügt werden):
# Verwendung
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2)) # Gibt aus: True
print(v1.__eq__(v3)) # Gibt aus: False
6.2 Liste aller Operatoren
Es gibt insgesamt 6 Gruppen von Operatoren, die überladen werden können.
Arithmetische Operatoren:
Operator | Methode ohne Unterstrich | Methodensignatur |
---|---|---|
+ | add |
__add__(self, other) |
- | sub |
__sub__(self, other) |
* | mul |
__mul__(self, other) |
/ | truediv |
__truediv__(self, other) |
// | floordiv |
__floordiv__(self, other) |
% | mod |
__mod__(self, other) |
** | pow |
__pow__(self, other) |
Vergleichsoperatoren:
Operator | Methode ohne Unterstrich | Methodensignatur |
---|---|---|
== | eq |
__eq__(self, other) |
!= | ne |
__ne__(self, other) |
< | lt |
__lt__(self, other) |
<= | le |
__le__(self, other) |
> | gt |
__gt__(self, other) |
>= | ge |
__ge__(self, other) |
Logische Operatoren:
Operator | Methode ohne Unterstrich | Methodensignatur |
---|---|---|
& | and |
__and__(self, other) |
| | or |
__or__(self, other) |
^ | xor |
__xor__(self, other) |
~ | invert |
__invert__(self) |
Indexierungs- und Slice-Operatoren:
Operator | Methode |
---|---|
obj[key] | __getitem__(self, key) |
obj[key] = value | __setitem__(self, key, value) |
del obj[key] | __delitem__(self, key) |
Unäre Operatoren:
Operator | Methode |
---|---|
- | __neg__(self) |
+ | __pos__(self) |
abs() | __abs__(self) |
~ | __invert__(self) |
Zuweisungsoperatoren:
Operator | Methode |
---|---|
+= | __iadd__(self, other) |
-= | __isub__(self, other) |
*= | __imul__(self, other) |
/= | __itruediv__(self, other) |
//= | __ifloordiv__(self, other) |
%= | __imod__(self, other) |
**= | __ipow__(self, other) |
Vielleicht ist Python deshalb so langsam – jedes Mal, bevor ein Operator ausgeführt wird, sucht es nach einer entsprechenden Funktion in der Klasse und allen seinen Elternklassen. Dafür kann man den kompaktesten Code der Welt schreiben :)
6.3 Der Indexierungsoperator
Dass man Objekte vergleichen oder Mengen subtrahieren kann, ist in gewissem Sinne offensichtlich. Und du wirst das selbst herausfinden, wenn du eine Klasse schreibst, die logische oder mathematische Operationen über sie impliziert.
Ich möchte mit dir solch ein interessantes Beispiel besprechen – den Indexierungsoperator. Lass uns gleich mit einem Beispielcode beginnen:
class CustomList:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __repr__(self):
return repr(self.data)
# Verwendung
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1]) # Gibt aus: 2
c_list[1] = 10
print(c_list) # Gibt aus: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list) # Gibt aus: [1, 3, 4, 5]
Hier sehen wir ein Beispiel für drei Operationen:
Lesen von Daten über einen Index
Schreiben von Daten über einen Index
Und sogar Löschen von Daten über einen Index.
Es ist überhaupt nicht notwendig, dass die Daten innen in Form einer Liste gespeichert werden. Oder dass der Index zwangsläufig eine Zahl sein muss. Zum Beispiel ist die Klasse dictionary
(Wörterbuch) genau so implementiert.
Erstellen wir eine SuperList
Erinnerst du dich an die Klasse list
? Man kann ihr Elemente zuweisen, aber nur mit den Indexen, die bereits existieren. Lass uns eine eigene Klasse erstellen, nennen wir sie SuperList
, deren Elemente mit jedem Index zugänglich sind:
Wenn der Index < 0 ist, fügen wir das Element am Anfang ein
Wenn der Index >= len ist, fügen wir das Element am Ende hinzu
In allen anderen Fällen geben wir einfach das Element zurück
Beispiel:
class SuperList(list):
def __init__(self, value):
super().__init__(value)
def __setitem__(self, index, value):
if index >= len(self):
super().append(value)
elif index < 0:
super().insert(0, value)
else:
super().__setitem__(index, value)
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst) # [99, 1, 2, 3, 100]
So ist die Überladung von Indizes eine großartige Möglichkeit, und ich empfehle, sie in der Praxis zu nutzen. Das war's für heute.
GO TO FULL VERSION